Introdução
Muitos programas úteis atravessam uma fronteira do sistema de arquivos: eles leem dados existentes, processam esses dados e salvam um resultado. Essas operações podem falhar. Por isso, as APIs de arquivos do Rust retornam valores Result, em vez de presumirem que o sucesso é garantido.
Você vai inspecionar um log de serviço preparado, representar locais de arquivos com Path, ler texto usando std::fs, criar um pequeno resumo, gravá-lo em outro arquivo e propagar erros de E/S com ?.
Ler um Arquivo de Texto por meio de um Path
Nesta etapa, você vai inspecionar um log preparado e carregá-lo em uma String do Rust.
O projeto está em /home/labex/project/log-summary. Ele contém data/events.log como entrada, um diretório output vazio para o relatório posterior e src/main.rs como código-fonte do programa. Entre no diretório do projeto:
cd /home/labex/project/log-summary
Abra o arquivo de entrada com o nano para saber o que o programa vai ler:
nano data/events.log
O arquivo contém três registros curtos. Não altere o arquivo; pressione Ctrl+X para sair. Agora abra o código-fonte do Rust:
nano src/main.rs
As duas linhas use já preparadas disponibilizam as funções de arquivo de std::fs e o tipo Path usando nomes abreviados. Elas servem apenas para configurar os imports e não são o tema principal deste laboratório; o laboratório posterior sobre módulos explica caminhos e declarações use em detalhes. O tipo de retorno std::io::Result<()> indica que main ou termina com o valor vazio de sucesso () ou retorna um erro de E/S. O Ok(()) final representa o resultado bem-sucedido.
Substitua o comentário da Etapa 1 por:
let input_path = Path::new("data/events.log");
let contents = fs::read_to_string(input_path)?;
println!("Input: {}", input_path.display());
println!("Contents:\n{}", contents.trim_end());
Path::new cria uma visualização emprestada do caminho do sistema de arquivos sem abrir nada. O caminho relativo é resolvido a partir do diretório do projeto em que você executa o Cargo. fs::read_to_string abre o arquivo, lê o texto UTF-8 em uma String própria e retorna um Result. O ? produz a String em caso de sucesso ou retorna o erro de E/S a partir de main. display() formata o caminho para exibição, e trim_end() impede que a quebra de linha final do arquivo crie uma linha de saída em branco adicional.
Salve com Ctrl+O, pressione Enter e saia com Ctrl+X. Verifique e execute o programa:
cargo check
cargo run --quiet
Input: data/events.log
Contents:
INFO service started
WARN disk nearing limit
INFO backup complete
O caminho exibido e os três registros confirmam que o programa leu o arquivo correto.
Contar Registros no Texto Carregado
Nesta etapa, você vai contar as linhas lógicas na String que já está na memória.
O método lines() cria um iterador sobre as linhas do texto sem alterar a string de origem. O consumidor count() retorna quantos itens o iterador produziu. Abra o código-fonte:
nano src/main.rs
Substitua o comentário da Etapa 2 por:
let line_count = contents.lines().count();
println!("Line count: {line_count}");
Salve e saia. Depois, execute:
cargo run --quiet
A nova última linha deve ser:
Line count: 3
Isso funciona independentemente de a entrada terminar ou não com uma quebra de linha, porque lines() identifica registros lógicos em vez de contar bytes de quebra de linha.
Criar uma String de Resumo Estável
Nesta etapa, você vai contar os registros de aviso e formatar as duas contagens em um texto pronto para ser salvo.
O método de string starts_with testa um prefixo. Ao combiná-lo com os adaptadores de iterador que você já conhece, o programa consegue contar apenas as linhas que começam com WARN. Abra o código-fonte:
nano src/main.rs
Substitua o comentário da Etapa 3 por:
let warning_count = contents
.lines()
.filter(|line| line.starts_with("WARN"))
.count();
let summary = format!("lines: {line_count}\nwarnings: {warning_count}\n");
println!("Summary ready:\n{}", summary.trim_end());
O predicado do filtro mantém as linhas de aviso, e count() consome esse iterador filtrado. format! retorna uma nova String em vez de imprimi-la; os caracteres \n explícitos criam duas linhas e deixam uma quebra de linha final adequada para um arquivo de texto.
Salve e saia. Depois, verifique e execute:
cargo check
cargo run --quiet
A seção final deve ser:
Summary ready:
lines: 3
warnings: 1
Agora o resumo está na memória como dados, pronto para atravessar a fronteira do sistema de arquivos.
Escrever e Ler Novamente o Arquivo de Resumo
Nesta etapa, você vai gravar o resumo em um diretório de saída preparado e lê-lo novamente como evidência.
fs::write(path, data) cria um arquivo ou substitui completamente o conteúdo antigo pelos bytes fornecidos; ele não adiciona conteúdo ao final. Assim como a leitura, a escrita pode falhar e retorna um Result de E/S. Por isso, você vai propagar o erro com ?. Abra o código-fonte:
nano src/main.rs
Substitua o comentário da Etapa 4 por:
let output_path = Path::new("output/summary.txt");
fs::write(output_path, &summary)?;
let saved = fs::read_to_string(output_path)?;
println!("Saved {}:\n{}", output_path.display(), saved.trim_end());
&summary empresta o resumo enquanto write lê seus bytes, portanto a String continua pertencendo ao programa. Ler o arquivo novamente verifica o artefato realmente salvo, em vez de presumir que a escrita funcionou.
Salve e saia. Depois, verifique e execute:
cargo check
cargo run --quiet
A seção final deve ser:
Saved output/summary.txt:
lines: 3
warnings: 1
Para uma última inspeção, abra o arquivo criado:
nano output/summary.txt
Você deve ver as mesmas duas linhas do resumo. Pressione Ctrl+X para sair sem fazer alterações.
Resumo
Você representou locais de arquivos com Path, leu texto UTF-8 com fs::read_to_string, propagou erros de E/S com ?, resumiu os registros carregados, gravou o resultado com fs::write e leu novamente o arquivo salvo para confirmar seu conteúdo.


