Comentários e documentação são partes essenciais de qualquer código-fonte. Em Rust, você pode adicionar comentários para explicar o funcionamento interno do código, além de gerar documentação automática a partir de doc comments. Esta aula cobre os tipos de comentários, a sintaxe de documentação e como usar a ferramenta cargo doc para produzir documentação HTML. Também exploraremos exemplos testáveis, que garantem que a documentação permaneça atualizada e funcional.

Comentários de linha e bloco

Em Rust, comentários de linha são iniciados por // e se estendem até o final da linha. Eles são usados para explicar trechos específicos de código, temporariamente desabilitar código ou adicionar notas. Já os comentários de bloco são delimitados por /* e */ e podem abranger várias linhas. Embora menos comuns, são úteis para comentar grandes blocos de código.

Exemplo de comentários de linha e bloco:

// Isto é um comentário de linha

/*
 Isto é um comentário de bloco
 que pode ocupar várias linhas
*/

fn main() {
    // Comentário de linha explicando a próxima linha
    let x = 5; // Comentário no final da linha
    /* Comentário de bloco dentro de uma função */
    println!("x é {}", x);
}

É importante notar que comentários de bloco podem ser aninhados? Não, em Rust comentários de bloco não podem ser aninhados. Se você tentar /* /* */ */, o primeiro */ encerra o comentário, causando um erro de sintaxe. Para comentar blocos que já contêm comentários, prefira usar // em cada linha.

Doc comments (///)

Doc comments são um tipo especial de comentário que geram documentação externa. Eles são escritos com três barras /// e podem ser colocados antes de itens como funções, structs, enums, módulos, etc. O conteúdo é interpretado como Markdown e pode incluir seções, exemplos, links e muito mais. Além disso, doc comments podem ser usados com //! para documentar o item que os contém (como um módulo ou crate).

Exemplo de doc comment para uma função:

/// Calcula a soma de dois números.
///
/// # Exemplo
///
/// ```
/// let resultado = soma(2, 3);
/// assert_eq!(resultado, 5);
/// ```
pub fn soma(a: i32, b: i32) -> i32 {
    a + b
}

Doc comments suportam tags comuns do Markdown, como # Exemplo, # Panics, # Errors, # Safety. Eles também podem conter links para outros itens da documentação usando colchetes: [`soma`].

cargo doc

O comando cargo doc gera a documentação HTML a partir dos doc comments do seu código. Ele usa o rustdoc internamente e produz uma página web para cada crate e seus itens. Para gerar a documentação, execute cargo doc no diretório do seu projeto. A documentação será salva em target/doc. Você pode abrir o arquivo target/doc/<nome_do_crate>/index.html no navegador.

Para visualizar a documentação diretamente, use cargo doc --open, que gera e abre a página no navegador. Além disso, você pode documentar seu crate com doc comments no arquivo lib.rs ou main.rs usando //!:

//! # Meu Crate
//!
//! Uma biblioteca para cálculos matemáticos.

/// Soma dois números.
pub fn soma(a: i32, b: i32) -> i32 {
    a + b
}

O cargo doc também gera documentação para dependências, a menos que você use a flag --no-deps. Para personalizar a documentação, você pode adicionar atributos como #![doc(html_logo_url = "...")] no crate root.

Exemplos testáveis

Uma das grandes vantagens dos doc comments em Rust é que você pode incluir exemplos de código que são executados como testes. Isso garante que os exemplos na documentação estejam sempre corretos. Para criar um exemplo testável, use a sintaxe de bloco de código com ``` e, opcionalmente, especifique ignore, no_run, compile_fail, etc., para controlar o comportamento.

Exemplo de um doc comment com um exemplo testável:

/// Calcula o fatorial de um número.
///
/// # Exemplo
///
/// ```
/// use meu_crate::fatorial;
///
/// assert_eq!(fatorial(5), 120);
/// ```
pub fn fatorial(n: u32) -> u32 {
    (1..=n).product()
}

Para executar os exemplos como testes, use cargo test. O rustdoc compila e executa cada bloco de código marcado como ``` (a menos que seja ignore). Se o exemplo falhar, o teste falha, mantendo a documentação sincronizada com o código.

Você também pode usar atributos como ```no_run para exemplos que não devem ser executados (ex.: código que faz I/O), ```compile_fail para exemplos que devem falhar na compilação, e ```edition2018 para especificar a edição do Rust.

Boas práticas

- Sempre documente funções públicas com doc comments, explicando o que fazem, parâmetros, retorno e possíveis panics.
- Use exemplos testáveis sempre que possível para garantir que a documentação não fique desatualizada.
- Para módulos e crates, use //! para fornecer uma visão geral.
- Mantenha os comentários de linha concisos e evite comentários óbvios que apenas repetem o código.

Referências

Exercícios

  1. Escreva um comentário de linha e um comentário de bloco em uma função main que imprime "Olá, mundo!". Use os comentários para explicar cada parte.

    ✓ Resposta:
    fn main() {
        // Isto é um comentário de linha
        println!("Olá, mundo!"); // Imprime a mensagem
        /*
            Isto é um comentário de bloco
            que pode ter várias linhas
        */
    }
  2. Crie uma função pública multiplica que recebe dois inteiros i32 e retorna o produto. Adicione um doc comment com um exemplo testável.

    ✓ Resposta:
    /// Multiplica dois números.
    ///
    /// # Exemplo
    ///
    /// ```
    /// use meu_crate::multiplica;
    ///
    /// assert_eq!(multiplica(4, 5), 20);
    /// ```
    pub fn multiplica(a: i32, b: i32) -> i32 {
        a * b
    }
  3. Use cargo doc para gerar a documentação de um crate simples que contém a função do exercício anterior. Explique o comando usado.

    ✓ Resposta:

    O comando é cargo doc --open. Ele gera a documentação HTML e a abre no navegador. A documentação será salva em target/doc/<nome_do_crate>/index.html.

  4. Escreva um doc comment para um módulo usando //! que descreve o propósito do módulo e inclui um exemplo de uso.

    ✓ Resposta:
    //! # Módulo de operações matemáticas
    //!
    //! Este módulo fornece funções básicas como soma e multiplicação.
    //!
    //! # Exemplo
    //!
    //! ```
    //! use meu_crate::operacoes;
    //! 
    //! let s = operacoes::soma(2, 3);
    //! assert_eq!(s, 5);
    //! ```
    
    pub fn soma(a: i32, b: i32) -> i32 {
        a + b
    }
    
    pub fn multiplica(a: i32, b: i32) -> i32 {
        a * b
    }
  5. No doc comment da função do exercício 2, modifique o exemplo para usar no_run e explique por que isso seria útil.

    ✓ Resposta:
    /// Multiplica dois números.
    ///
    /// # Exemplo
    ///
    /// ```no_run
    /// use meu_crate::multiplica;
    ///
    /// let resultado = multiplica(4, 5);
    /// println!("Resultado: {}", resultado);
    /// ```
    pub fn multiplica(a: i32, b: i32) -> i32 {
        a * b
    }

    O atributo no_run impede que o exemplo seja executado durante cargo test, mas ainda assim é compilado. Isso é útil quando o exemplo realiza operações com efeitos colaterais, como I/O, ou quando a execução não é necessária para verificar a correção.