43

I just want a space separated String of the argument variables obtained from std::env::args(), which I've been creating using the fold function like this:

std::env::args()
    .fold("".to_string(), |accum, s| accum + &s + " ")

However this creates an extra space at the end which is unwanted. I tried using the truncate function, but truncate doesn't return a String, just modifies the existing String, and also this would require the creation of an intermediate binding in order to use the String's len() call to define how long the truncated String should be (which itself would require an intermediate binding due to Rust's current lexical borrowing rules I believe!)

8
  • 1
    @Ferruccio This just results in a leading space, I only did it the way around I did because a trailing space is often invisible whereas a leading one can be seen in the formatting. I should have specified leading and trailing spaces are unwanted though. Commented Apr 29, 2016 at 15:14
  • 3
    There's an intersperse method in itertools if you're willing to add a dependency. I don't think there's a straightforward way to do this with std. Commented Apr 29, 2016 at 15:18
  • 1
    Yes, of course, I should have realized that would happen. I've run into this problem before (using C#). You need to check if accum is empty. I'm not sure if this is valid Rust syntax, but I normally do something like: accum + (if accum == "" {""} else {" "}) + &s Commented Apr 29, 2016 at 15:32
  • 3
    @Ferruccio Yes that would work, slightly messier looking closure but nothing too dreadful. Just a note for anyone choosing the itertools approach though - there's a join function in itertools which joins all elements into a single String separated by a specified separator. Commented Apr 29, 2016 at 15:46
  • 5
    Why all this talk of intersperse in itertools (I see it often)? Itertools has actual methods for formatting, use .join(" ") or .format_default(" ") Commented Apr 29, 2016 at 23:39

5 Answers 5

31

What's an idiomatic way to print all command line arguments in Rust?

fn main() {
    let mut args = std::env::args();

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

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

Or better with Itertools' format or format_with:

use itertools::Itertools; // 0.8.0

fn main() {
    println!("{}", std::env::args().format(" "));
}

I just want a space separated String

use itertools::{Itertools, Position};

fn args() -> String {
    let mut result = String::new();
    let args = std::env::args();

    for (position, arg) in args.with_position() {
        match position {
            Position::First | Position::Only => result.push_str(&arg),
            _ => {
                result.push(' ');
                result.push_str(&arg);
            }
            
        }
    }

    result
}

fn main() {
    println!("{}", args());
}

Or similarly, you can use .enumerate() and check if the index is 0 if not using Itertools.

Or

fn args() -> String {
    let mut result = String::new();
    let mut args = std::env::args();

    if let Some(arg) = args.next() {
        result.push_str(&arg);

        for arg in args {
            result.push(' ');
            result.push_str(&arg);
        }
    }

    result
}

fn main() {
    println!("{}", args());
}

Or

fn args() -> String {
    let mut result = std::env::args().fold(String::new(), |s, arg| s + &arg + " ");
    result.pop();
    result
}

fn main() {
    println!("{}", args());
}

If you use Itertools, you can use the format / format_with examples above with the format! macro.

join is also useful:

use itertools::Itertools; // 0.8.0

fn args() -> String {
    std::env::args().join(" ")
}

fn main() {
    println!("{}", args());
}

In other cases, you may want to use intersperse:

use itertools::Itertools; // 0.8.0

fn args() -> String {
    std::env::args().intersperse(" ".to_string()).collect()
}

fn main() {
    println!("{}", args());
}

Note this isn't as efficient as other choices as a String is cloned for each iteration.

Sign up to request clarification or add additional context in comments.

5 Comments

If you put the for loop inside the if let, you do not need to use fuse.
With itertools, you can just do std::env::args().join(" ") (thanks to OP and @bluss for the tip in the question comments).
For the intersperse example, couldn't you just call .collect::<String>() instead of folding?
@Addison yep! I'm guessing I copy-pasted from the earlier example with fold and didn't apply critical thought.
"this isn't as efficient..." - Referring to the join and intersperse or fold or which ones? And why aren't they ( if it's join or intersperse) doing sth efficient internally? For strings, I cannot see a reason for intersperse compared to join from a quick look (maybe if you specifically don't want to automatically use the Display trait?), but it is useful to intersperse other data types and there is even an intersperse_with that takes a function.
7

Here's a solution with iterators, and zero non-stdlib dependencies

(performance wise, this shines in release mode, with optimizations turned on [playground])

use std::iter::once;

fn join_iter<T>(
    mut iter: impl Iterator<Item = T>,
    sep: impl Fn(&T) -> T,
) -> impl Iterator<Item = T> {
    iter.next()
        .into_iter()
        .chain(iter.flat_map(move |s| once(sep(&s)).chain(once(s))))
}

fn examples() {
    let iter = [1, 3, 5, 7, 9].iter().cloned();
    println!("{:?}", join_iter(iter, |v| v - 1).collect::<Vec<_>>());
    // [1, 2, 3, 4, 5, 6, 7, 8, 9]

    let iter = ["Hello", "World"].iter().cloned();
    let sep = ", ";
    println!("{:?}", join_iter(iter, |_| sep).collect::<String>());
    // "Hello, World"
}

fn args() -> String {
    join_iter(std::env::args(), |_| " ".to_string()).collect()
}

fn main() {
    examples();

    println!("{}", args());
}

Comments

4

Here is an alternative for

I just want a space separated String of the argument variables obtained from std::env::args()

fn args_string() -> String {
    let mut out = String::new();
    let mut sep = "";
    for arg in std::env::args() {
        out.push_str(sep);
        out.push_str(&*arg);
        sep = " ";
    }
    out
}

pub fn main() {
    println!("{}", args_string());
}

Comments

2

Here is a version using fold

fn join<I, T, D>(iter: I, separator: D) -> String
where
    T: Display,
    D: Display,
    I: Iterator<Item = T>,
{
    match iter.fold(None, |a:Option<String>, b| {
        Some(match a {
            None => format!("{}", &b),
            Some(mut a) => {
                write!(a, "{}{}", separator, b).unwrap();
                a
            }
        })
    }) {
        None => String::new(),
        Some(rval) => rval,
    }
}

1 Comment

A nicer way to special case the first iteration is with with_position from itertools.
-1
fn main() {
    println!("{}", std::env::args().collect::<Vec<_>>().join(" "));
}

1 Comment

Remember that Stack Overflow isn't just intended to solve the immediate problem, but also to help future readers find solutions to similar problems, which requires understanding the underlying code. This is especially important for members of our community who are beginners, and not familiar with the syntax. Given that, can you edit your answer to include an explanation of what you're doing and why you believe it is the best approach?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.