Can this "echo" clone be improved?

Hi there. I'm an experienced programmer, but very new to Rust. I have written a Rust version of echo for trying out the language. It's already been improved a lot from the initial version, and below is the best I could come up with:

fn main() {
    let args: Vec<String> = std::env::args().skip(1).collect();
    if args.len() > 0 {
        if args[0] == "-n" {
            print!("{}", args[1..].join(" "));
        } else {
            println!("{}", args.join(" "));
        }
    }
}

In case you are not aware, echo accepts a -n flag which tells it to not print a new line.

I think the final version is pretty concise. I can't help but wonder though if there's a better way to write this, perhaps a more "idiomatic" way. If so, how would you change my implementation?

Here's a cute implementation built on slice patterns:

fn main() {
    let args: Vec<_> = std::env::args().skip(1).collect();
    let args: Vec<_> = args.iter().map(|s| s.as_str()).collect();

    let (newline, words) = match args.as_slice() {
        ["-n", rest @ ..] => (false, rest),
        args => (true, args),
    };

    print!("{}", words.join("\n"));

    if newline {
        println!();
    }
}

(playground)

It doesn't really make sense for your example, but doing match on a slice of strings (&str, not String) can be quite expressive when used in the right place.

1 Like

In general, if you've got an iterator, it's not recommended to just collect it unconditionally into a vector unless you need all the values at the same time (i.e. random access). Even if you do have a vector/array/slice, it's best not to check for emptyness explicitly and then index into it; you can perform most operations with pattern matching or .get() instead of panicking indexing.

fn main() {
    let mut args = std::env::args().skip(1).peekable();
    
    let newline = if let Some("-n") = args.peek().map(AsRef::as_ref) {
        args.next();
        false
    } else {
        true
    };

    if let Some(first) = args.next() {
        print!("{}", first);
    }

    for arg in args {
        print!(" {}", arg);
    }

    if newline { println!(); }
}
2 Likes

Traditionally, echo with no arguments does print a newline, which your version does not. The version by @H2CO3 fixes this.

1 Like

Nice, thanks for the tips.

if newline() { println!(); }

The () need to be removed from here, right?

Yes, indeed. Fixed.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.