If given a Vec<String> of values, how can I get back a String where each has been prepended with -a . Here is my solution, and it works, but it feels obtuse, so I'd like to know the best way to do this.
input: ["foo", "bar"]
output: -a foo -a bar
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5558637f3462414cd57926db0faaf20b
fn f(args: Vec<String>) -> String {
args.into_iter()
.map(|s| format!("-a {}", s))
.collect::<Vec<String>>().join(" ")
}
fn main() {
let args = ["foo", "bar"].iter().map(|&s| s.into()).collect();
println!("{}", f(args));
}
It's concise but there are two points of inefficiency here:
creating a new vec instead of modifying the vec in place
creating a lot of strings when you just want one string
A first improvement would be to just prepend to the strings in place but if you want something more direct you could do
fn f(args: Vec<String>) -> String {
let mut s = String::new();
for arg in args {
if !s.is_empty() {
s.push(' ');
}
s.push_str("-a ");
s.push_str(&arg);
}
s
}
If performance is irrelevant for your use case, then there's no problem with your code. Without additional crates join does need a vec today (you could use intersperse but only in nightly).
If you can accept a slight loss of speed - here is a shorter solution using fold:
fn f(args: Vec<String>) -> String {
args.iter()
.fold(String::new(), |s1, s2| s1 + " -a " + s2)
.trim_start().to_string()
}
Related
This question already has answers here:
What's an idiomatic way to print an iterator separated by spaces in Rust?
(4 answers)
Closed 2 years ago.
I want to join a list of numbers into a String. I have the following code:
let ids: Vec<i32> = Vec::new();
ids.push(1);
ids.push(2);
ids.push(3);
ids.push(4);
let joined = ids.join(",");
print!("{}", joined);
However, I get the following compilation error:
error[E0599]: no method named `join` found for struct `std::vec::Vec<i32>` in the current scope
--> src\data\words.rs:95:22
|
95 | let joined = ids.join(",");
| ^^^^ method not found in `std::vec::Vec<i32>`
|
= note: the method `join` exists but the following trait bounds were not satisfied:
`<[i32] as std::slice::Join<_>>::Output = _`
I'm a bit unclear as to what to do. I understand the implementation of traits, but whatever trait it's expecting, I would expect to be natively implemented for i32. I would expect joining integers into a string to be more trivial than this. Should I cast all of them to Strings first?
EDIT: It's not the same as the linked question, because here I am specifically asking about numbers not being directly "joinable", and the reason for the trait to not be implemented by the number type. I looked fairly hard for something in this direction and found nothing, which is why I asked this question.
Also, it's more likely that someone will search specifically for a question phrased like this instead of the more general "idiomatic printing of iterator values".
I would do
let ids = vec!(1,2,3,4);
let joined: String = ids.iter().map( |&id| id.to_string() + ",").collect();
print!("{}", joined);
Generally when you have a collection of one type in Rust, and want to turn it to another type, you call .iter().map(...) on it. The advantage of this method is you keep your ids as integers which is nice, have no mutable state, and don't need an extra library. Also if you want a more complex transformation than just a casting, this is a very good method. The disadvantage is you have a trailing comma in joined. playground link
If you don't want to explicitly convert into string, then you can use Itertools::join method (this is an external crate though)
PlayGround
Relevant code:
use itertools::Itertools;
let mut ids: Vec<i32> = ...;
let joined = Itertools::join(&mut ids.iter(), ",");
print!("{}", joined);
Frxstrem suggestion:
let joined = ids.iter().join(".");
Using the [T]::join() method requires that [T] implements the Join trait. The Join trait is only implemented for [T] where T implements Borrow<str> (like String or &str) or Borrow<[U]> (like &[U] or Vec<U>). In other words, you can only join a vector of strings or a vector of slices/vectors.
In general, Rust requires you to be very explicit about type conversion, so in many cases you shouldn't expect the language to e.g. automatically convert an integer to a string for you.
To solve your problem, you need to explicitly convert your integers into strings before pushing them into your vector:
let mut ids: Vec<String> = Vec::new();
ids.push(1.to_string());
ids.push(2.to_string());
ids.push(3.to_string());
ids.push(4.to_string());
let joined = ids.join(",");
print!("{}", joined);
Playground example
If you want a generic solution:
fn join<I, T>(it: I, sep: &str) -> String
where
I: IntoIterator<Item = T>,
T: std::fmt::Display,
{
use std::fmt::Write;
let mut it = it.into_iter();
let first = it.next().map(|f| f.to_string()).unwrap_or_default();
it.fold(first, |mut acc, s| {
write!(acc, "{}{}", sep, s).expect("Writing in a String shouldn't fail");
acc
})
}
fn main() {
assert_eq!(join(Vec::<i32>::new(), ", "), "");
assert_eq!(join(vec![1], ", "), "1");
assert_eq!(join(vec![1, 2, 3, 4], ", "), "1, 2, 3, 4");
}
Maybe this implement
If you prefer that style, you can use an extension method:
trait JoinIterator {
fn join(self, sep: &str) -> String;
}
impl<I, T> JoinIterator for I
where
I: IntoIterator<Item = T>,
T: std::fmt::Display,
{
fn join(self, sep: &str) -> String {
use std::fmt::Write;
let mut it = self.into_iter();
let first = it.next().map(|f| f.to_string()).unwrap_or_default();
it.fold(first, |mut acc, s| {
write!(acc, "{}{}", sep, s).expect("Writing in a String shouldn't fail");
acc
})
}
}
fn main() {
assert_eq!(Vec::<i32>::new().join(", "), "");
assert_eq!(vec![1].join(", "), "1");
assert_eq!(vec![1, 2, 3, 4].join(", "), "1, 2, 3, 4");
}
This question already has answers here:
What's an idiomatic way to print an iterator separated by spaces in Rust?
(4 answers)
Closed 3 years ago.
How do I convert an Iterator<&str> to a String, interspersed with a constant string such as "\n"?
For instance, given:
let xs = vec!["first", "second", "third"];
let it = xs.iter();
One may produce a string s by collecting into a Vec<&str> and joining the result:
let s = it
.map(|&x| x)
.collect::<Vec<&str>>()
.join("\n");
However, this unnecessarily allocates memory for a Vec<&str>.
Is there a more direct method?
You could use the itertools crate for that. I use the intersperse helper in the example, it is pretty much the join equivalent for iterators.
cloned() is needed to convert &&str items to &str items, it is not doing any allocations. It can be eventually replaced by copied() when rust#1.36 gets a stable release.
use itertools::Itertools; // 0.8.0
fn main() {
let words = ["alpha", "beta", "gamma"];
let merged: String = words.iter().cloned().intersperse(", ").collect();
assert_eq!(merged, "alpha, beta, gamma");
}
Playground
You can do it by using fold function of the iterator easily:
let s = it.fold(String::new(), |a, b| a + b + "\n");
The Full Code will be like following:
fn main() {
let xs = vec!["first", "second", "third"];
let it = xs.into_iter();
// let s = it.collect::<Vec<&str>>().join("\n");
let s = it.fold(String::new(), |a, b| a + b + "\n");
let s = s.trim_end();
println!("{:?}", s);
}
Playground
EDIT: After the comment of Sebastian Redl I have checked the performance cost of the fold usage and created a benchmark test on playground.
You can see that fold usage takes significantly more time for the many iterative approaches.
Did not check the allocated memory usage though.
there's relevant example in rust documentation: here.
let words = ["alpha", "beta", "gamma"];
// chars() returns an iterator
let merged: String = words.iter()
.flat_map(|s| s.chars())
.collect();
assert_eq!(merged, "alphabetagamma");
You can also use Extend trait:
fn f<'a, I: Iterator<Item=&'a str>>(data: I) -> String {
let mut ret = String::new();
ret.extend(data);
ret
}
This solution seems rather inelegant:
fn parse_range(&self, string_value: &str) -> Vec<u8> {
let values: Vec<u8> = string_value
.splitn(2, "-")
.map(|part| part.parse().ok().unwrap())
.collect();
{ values[0]..(values[1] + 1) }.collect()
}
Since splitn(2, "-") returns exactly two results for any valid string_value, it would be better to assign the tuple directly to two variables first and last rather than a seemingly arbitrary-length Vec. I can't seem to do this with a tuple.
There are two instances of collect(), and I wonder if it can be reduced to one (or even zero).
Trivial implementation
fn parse_range(string_value: &str) -> Vec<u8> {
let pos = string_value.find(|c| c == '-').expect("No valid string");
let (first, second) = string_value.split_at(pos);
let first: u8 = first.parse().expect("Not a number");
let second: u8 = second[1..].parse().expect("Not a number");
{ first..second + 1 }.collect()
}
Playground
I would recommend returning a Result<Vec<u8>, Error> instead of panicking with expect/unwrap.
Nightly implementation
My next thought was about the second collect. Here is a code example which uses nightly code, but you won't need any collect at all.
#![feature(conservative_impl_trait, inclusive_range_syntax)]
fn parse_range(string_value: &str) -> impl Iterator<Item = u8> {
let pos = string_value.find(|c| c == '-').expect("No valid string");
let (first, second) = string_value.split_at(pos);
let first: u8 = first.parse().expect("Not a number");
let second: u8 = second[1..].parse().expect("Not a number");
first..=second
}
fn main() {
println!("{:?}", parse_range("3-7").collect::<Vec<u8>>());
}
Instead of calling collect the first time, just advance the iterator:
let mut values = string_value
.splitn(2, "-")
.map(|part| part.parse().unwrap());
let start = values.next().unwrap();
let end = values.next().unwrap();
Do not call .ok().unwrap() — that converts the Result with useful error information to an Option, which has no information. Just call unwrap directly on the Result.
As already mentioned, if you want to return a Vec, you'll want to call collect to create it. If you want to return an iterator, you can. It's not bad even in stable Rust:
fn parse_range(string_value: &str) -> std::ops::Range<u8> {
let mut values = string_value
.splitn(2, "-")
.map(|part| part.parse().unwrap());
let start = values.next().unwrap();
let end = values.next().unwrap();
start..end + 1
}
fn main() {
assert!(parse_range("1-5").eq(1..6));
}
Sadly, inclusive ranges are not yet stable, so you'll need to continue to use +1 or switch to nightly.
Since splitn(2, "-") returns exactly two results for any valid string_value, it would be better to assign the tuple directly to two variables first and last rather than a seemingly arbitrary-length Vec. I can't seem to do this with a tuple.
This is not possible with Rust's type system. You are asking for dependent types, a way for runtime values to interact with the type system. You'd want splitn to return a (&str, &str) for a value of 2 and a (&str, &str, &str) for a value of 3. That gets even more complicated when the argument is a variable, especially when it's set at run time.
The closest workaround would be to have a runtime check that there are no more values:
assert!(values.next().is_none());
Such a check doesn't feel valuable to me.
See also:
What is the correct way to return an Iterator (or any other trait)?
How do I include the end value in a range?
I want to split a string by a separator only once and put it into a tuple. I tried doing
fn splitOnce(in_string: &str) -> (&str, &str) {
let mut splitter = in_string.split(':');
let first = splitter.next().unwrap();
let second = splitter.fold("".to_string(), |a, b| a + b);
(first, &second)
}
but I keep getting told that second does not live long enough. I guess it's saying that because splitter only exists inside the function block but I'm not really sure how to address that. How to I coerce second into existing beyond the function block? Or is there a better way to split a string only once?
You are looking for str::splitn:
fn split_once(in_string: &str) -> (&str, &str) {
let mut splitter = in_string.splitn(2, ':');
let first = splitter.next().unwrap();
let second = splitter.next().unwrap();
(first, second)
}
fn main() {
let (a, b) = split_once("hello:world:earth");
println!("{} --- {}", a, b)
}
Note that Rust uses snake_case.
I guess it's saying that because splitter only exists inside the function block
Nope, it's because you've created a String and are trying to return a reference to it; you cannot do that. second is what doesn't live long enough.
How to I coerce second into existing beyond the function block?
You don't. This is a fundamental aspect of Rust. If something needs to live for a certain mount of time, you just have to make it exist for that long. In this case, as in the linked question, you'd return the String:
fn split_once(in_string: &str) -> (&str, String) {
let mut splitter = in_string.split(':');
let first = splitter.next().unwrap();
let second = splitter.fold("".to_string(), |a, b| a + b);
(first, second)
}
str::split_once is now built-in.
Doc examples:
assert_eq!("cfg".split_once('='), None);
assert_eq!("cfg=".split_once('='), Some(("cfg", "")));
assert_eq!("cfg=foo".split_once('='), Some(("cfg", "foo")));
assert_eq!("cfg=foo=bar".split_once('='), Some(("cfg", "foo=bar")));
I want to get the first character of a std::str. The method char_at() is currently unstable, as is String::slice_chars.
I have come up with the following, but it seems excessive to get a single character and not use the rest of the vector:
let text = "hello world!";
let char_vec: Vec<char> = text.chars().collect();
let ch = char_vec[0];
UTF-8 does not define what "character" is so it depends on what you want. In this case, chars are Unicode scalar values, and so the first char of a &str is going to be between one and four bytes.
If you want just the first char, then don't collect into a Vec<char>, just use the iterator:
let text = "hello world!";
let ch = text.chars().next().unwrap();
Alternatively, you can use the iterator's nth method:
let ch = text.chars().nth(0).unwrap();
Bear in mind that elements preceding the index passed to nth will be consumed from the iterator.
I wrote a function that returns the head of a &str and the rest:
fn car_cdr(s: &str) -> (&str, &str) {
for i in 1..5 {
let r = s.get(0..i);
match r {
Some(x) => return (x, &s[i..]),
None => (),
}
}
(&s[0..0], s)
}
Use it like this:
let (first_char, remainder) = car_cdr("test");
println!("first char: {}\nremainder: {}", first_char, remainder);
The output looks like:
first char: t
remainder: est
It works fine with chars that are more than 1 byte.
Get the first single character out of a string w/o using the rest of that string:
let text = "hello world!";
let ch = text.chars().take(1).last().unwrap();
It would be nice to have something similar to Haskell's head function and tail function for such cases.
I wrote this function to act like head and tail together (doesn't match exact implementation)
pub fn head_tail<T: Iterator, O: FromIterator<<T>::Item>>(iter: &mut T) -> (Option<<T>::Item>, O) {
(iter.next(), iter.collect::<O>())
}
Usage:
// works with Vec<i32>
let mut val = vec![1, 2, 3].into_iter();
println!("{:?}", head_tail::<_, Vec<i32>>(&mut val));
// works with chars in two ways
let mut val = "thanks! bedroom builds YT".chars();
println!("{:?}", head_tail::<_, String>(&mut val));
// calling the function with Vec<char>
let mut val = "thanks! bedroom builds YT".chars();
println!("{:?}", head_tail::<_, Vec<char>>(&mut val));
NOTE: The head_tail function doesn't panic! if the iterator is empty. If this matched Haskell's head/tail output, this would have thrown an exception if the iterator was empty. It might also be good to use iterable trait to be more compatible to other types.
If you only want to test for it, you can use starts_with():
"rust".starts_with('r')
"rust".starts_with(|c| c == 'r')
I think it is pretty straight forward
let text = "hello world!";
let c: char = text.chars().next().unwrap();
next() takes the next item from the iterator
To “unwrap” something in Rust is to say, “Give me the result of the computation, and if there was an error, panic and stop the program.”
The accepted answer is a bit ugly!
let text = "hello world!";
let ch = &text[0..1]; // this returns "h"