6

How to do something similar to this D and Java code in Rust?

Java:

import java.nio.file.*;
import java.io.*;

public class Main {
    public static void main( String[] args ) throws IOException
    {
        Files.lines(Paths.get("/home/kozak/test.txt"))
            .filter(s -> s.endsWith("/bin/bash"))
            .map(s -> s.split(":", 2)[0])
            .forEach(System.out::println);
    }
}

D language:

import std.algorithm;
import std.stdio;

void main() {
    File("/home/kozak/test.txt")
        .byLine
        .filter!((s)=>s.endsWith("/bin/bash"))
        .map!((s)=>s.splitter(":").front)
        .each!writeln;
}

I try it, but I am lost with all this ownership stuff

my rust code:

use std::io::BufReader;
use std::fs::File;
use std::io::BufRead;
use std::io::Lines;

fn main() {
    let file = match File::open("/etc/passwd") {
        Ok(file) => file,
        Err(..)  => panic!("room"),
    };

    let mut reader = BufReader::new(&file);
    for line in reader.lines().filter_map(
        |x| if match x { Ok(v) => v.rmatches("/bin/bash").count() > 0, Err(e) => false}
        { match x { Ok(v2) => Some(v2.split(":").next()), Err(e2) => None }} else
        { None })
    { 
            print!("{}", line.unwrap() );

    }
}
Kozzi11
  • 2,413
  • 12
  • 17
  • 2
    And what have you done so far? You should have enough experience to understand that SO is not a site where you just drop your work "please do that for me" on other people. – GhostCat May 19 '15 at 14:11
  • my fault I click post too early and do not realize It is missing my try – Kozzi11 May 19 '15 at 14:48

1 Answers1

8

Here you go:

use std::fs::File;
use std::io::{BufRead, BufReader};

fn main() {
    let f = BufReader::new(File::open("/etc/passwd").unwrap());
    let it = f.lines()
        .map(|line| line.unwrap())
        .filter(|line| line.ends_with("/bin/bash"))
        .map(|line| line.split(":").next().unwrap().to_owned());
    for p in it {
        println!("{}", p);
    }
}

This code allocates a separate string for each first splitted part though, but I don't think it is possible to avoid it without streaming iterators. And, of course, error handling here is really lax.

I guess an imperative approach would be more idiomatic, especially in regard to error handling:

use std::fs::File;
use std::io::{BufRead, BufReader};

fn main() {
    let f = BufReader::new(File::open("/etc/passwd").unwrap());
    for line in f.lines() {
        match line {
            Ok(line) => if line.ends_with("/bin/bash") {
                if let Some(name) = line.split(":").next() {
                    println!("{}", name);
                } else {
                    println!("Line does not contain ':'");
                }
            },
            Err(e) => panic!("Error reading file: {}", e)
        }
    }
}
Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
  • nice thanks a lot what do you think about this solution: http://www.abclinuxu.cz/zpravicky/rust-1.0#58 – Kozzi11 May 19 '15 at 15:03
  • @Kozzi11, looks good. Formatting is a bit off though, and if you insist on using iterators you'd better use some convenient iterator adapters from [itertoos](https://crates.io/crates/itertools), like `foreach()`, so you won't need to hackily force the iterator to run with `last()` or something. – Vladimir Matveev May 19 '15 at 18:10
  • @VladimirMatveev My formatting is unorthodox, yes :) Thanks for mentioning itertools, didn't know that... – kralyk May 19 '15 at 21:37