Paulo Henrique Rodrigues Pinheiro

Um blog sobre programação para programadores!


Rust Pills - Agindo como um filtro unix

Nesta dica será apresentada uma forma de ler a entrada padrão (stdin), em um programa Rust.

Rust pills

Há problemas em que é mais simples ler a entrada padrão, processar e mostrar alguma saída. Bem mais prático que receber o nome de um arquivo como parâmetro, abrir o arquivo, ler o arquivo, processar e então mostrar a saída.

Pipes são uma facilidade muito presente no mundo Unix.

Eu me bati um pouco pra conseguir isso, e talvez essa não seja a melhor solução, mas faz o trabalho. Ler a entrada padrão (stdin) e escrever para saída padrão (stdout):


use std::io;
use std::io::prelude::*;

fn main() {
    let stdin = io::stdin();

    for l in stdin.lock().lines() {
        let line = l.unwrap();
        println!("{}", line);
    }
}

A linha duas primeiras linhas nos permitem ter acesso ao que precisamos para processar a entrada padrão. Dentro do main, criamos um handler para a entrada padrão (stdin). e logo após, no for lemos linha a linha.

Ao compilar e executar (salvei como cat.rs), temos isso:


$ rustc cat.rs
$ ls /|./cat
bin
boot
dev
etc
home
lib
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

Se não passar nenhum "fluxo" para o comando, ele irá imprimir o que você digitar, até que sejam digitadas as teclas CONTROL e D.

Mas no meu problema concreto, eu devo ler cada linha, executar um processamento, e se uma condição for satisfeita, imprimir a linha e o resultado do processamento.

Aí a coisa pegou.

Pois no Rust, uma vez que um conteúdo é "consumido", ele não existe mais. E é isso que acontecia no meu problema, pois eu precisa passar a linha como um vetor de u8 para minha função de processamento, e ao fazer essa conversão, eu não tinha mais acesso à linha.

Mas depois de algumas releituras do manual, da documentação, de artigos e tutoriais, cheguei a esse resultado:


use std::io;
use std::io::prelude::*;

fn begin_with_b(array: &[u8]) -> bool {
    array[0] as char == 'b'
}

fn main() {
    let stdin = io::stdin();

    for l in stdin.lock().lines() {
        let line = l.unwrap();
        let bytes = line.as_bytes();

        if begin_with_b(bytes) {
            println!("{}", line);
        }
    }
}

Executando (salvei como filter.rs):


$ rustc filter.rs
$ ls /|./filter
bin
boot