pretty printing a Vec<char> with a separator [duplicate] - rust

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.
I am trying to apply join (or something similar) to a Vec<char> in order to pretty print it.
What I came up with so far is this (and this does what I want):
let vec: Vec<char> = "abcdef".chars().collect();
let sep = "-";
let vec_str: String = vec
.iter().map(|c| c.to_string()).collect::<Vec<String>>().join(sep);
println!("{}", vec_str); // a-b-c-d-e-f
That seems overly complex (and allocates a Vec<String> that is not really needed).
I also tried to get std::slice::join to work by explicitly creating a slice:
let vec_str: String = (&vec[..]).join('-');
but here the compiler complains:
method not found in &[char]
Is there a simpler way to create a printable String from a Vec<char> with a separator between the elements?

You can use intersperse from the itertools crate.
use itertools::Itertools; // 0.8.2
fn main() {
let vec : Vec<_> = "abcdef".chars().collect();
let sep = '-';
let sep_str : String = vec.iter().intersperse(&sep).collect();
println!("{}", sep_str);
}
Playground

Related

How do I use unwrap_or to return a string reference? [duplicate]

This question already has an answer here:
Returning a default &str for HashMap<_, String> [duplicate]
(1 answer)
Closed 9 months ago.
So this is my situation:
let map: HashMap<String, String> = HashMap::new();
let key = String::from("key");
let v = map.get(&key).unwrap_or(&String::from("key not found"));
println!("{}", v);
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1cab10de5528149c5d4ac37d9e0964f0
And this is my error:
temporary value is freed at the end of this statement creates a temporary which is freed while still in use
Being quite new to Rust I'm not sure if I'm using String properly or if str or &str would be more appropriate, but in any case I'm not exactly sure how I could use unwrap_or in this case to return a reference to a String. I know I could use pattern matching but I would prefer this pattern.
How do I use unwrap_or to return a string reference?
Short answer: you do not unless is a &'static str
For your case, you are creating an String already, so return a String and then use it whenever you need the &str. Also as nitpick, you would probably should use unwrap_or_else to avoid allocating the String in case the item is found:
let map: HashMap<String, String> = HashMap::new();
let v = map.get(&key).unwrap_or_else(|| String::from("key not found"));
println!("{}", v);

How to create a &str from a single character? [duplicate]

This question already has answers here:
Converting a char to &str
(3 answers)
Closed 1 year ago.
I can't believe I'm asking this frankly, but how do I create a &str (or a String) when I have a single character?
The first thing to try for simple conversions is into().
It works for String because String implements From<char>.
let c: char = 'π';
let s: String = c.into();
You can't build a &str directly from a char. A &str is a reference type. The easiest solution is to build it from a string:
let s: &str = &s;
An alternative for most kinds of values is the format macro:
let s = format!("{}", c);
If just need to use the &str locally and you want to avoid heap allocation, you can use char method encode_utf8:
fn main() {
let c = 'n';
let mut tmp = [0; 1];
let foo = c.encode_utf8(&mut tmp);
println!("str: {}", foo);
}
or
fn main() {
let tmp = [b'n'; 1];
let foo = std::str::from_utf8(&tmp).unwrap();
println!("str: {}", foo);
}
To work with every char you need to use a u8 array of length 4 [0; 4]. In utf8, ascii chars can be represented as a single byte, but all other characters require more bytes with maximum of 4.
This is a simplified example based on an answer from a very similar question:
Converting a char to &str

Is this the Correct way to make mutable variable immutable again in rust? [duplicate]

This question already has answers here:
What does 'let x = x' do in Rust?
(2 answers)
Closed 2 years ago.
I've created a simple list that has mutable push behaviour but no need to have that same mutability for peek fn
fn peek(){
let mut list = List::new();//take a mutable ref here to perform push
list.push(1);
let list = list; //shadow the variable with the same name and take a immut ref
assert_eq!(list.peek().unwrap(),&1);
}
Your way is fine. It's typical to include a comment like this:
let list = list; // discard mut
Another way to do it (it's arguable whether it's better or not, complexity vs. readability vs. separation of concerns) is to separate the initialization.
fn peek() {
let list = {
let mut list = List::new();
list.push(1);
list
};
assert_eq!(list.peek().unwrap(),&1);
}
Your solution is correct enough. Maybe it would be a bit more readable to do it this way:
fn peek(){
let list = {
let mut li = List::new();//take a mutable ref here to perform push
li.push(1);
li
};
assert_eq!(list.peek().unwrap(),&1);
}
But in your case there is another option:
fn peek(){
let list = std::iter::once(1).collect::<List<_>>();
assert_eq!(list.peek().unwrap(),&1);
}

Join iterator of &str [duplicate]

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
}

How do I sort a vector of Strings alphabetically? [duplicate]

This question already has answers here:
How do I sort an array?
(1 answer)
Case-insensitive string matching in Rust
(2 answers)
Closed 3 years ago.
I want to order a Strings vector alphabetically
fn main() {
let mut vec = Vec::new();
vec.push("richard");
vec.push("charles");
vec.push("Peter");
println!("{:?}", vec);
}
I tried println!("{:?}", vec.sort()); and println!("{}", vec.sort_by(|a,b| b.cmp(a))); and both response is ().
And I expect the following result
["charles", "Peter", "richard"]
sort function is defined on slices (and on Vecs, as they can Deref to slices) as pub fn sort(&mut self), i.e. it performs sorting in place, mutating the existing piece of data. So to achieve what you're trying to do, you can try the following:
fn main() {
let mut vec = Vec::new();
vec.push("richard");
vec.push("charles");
vec.push("Peter");
vec.sort();
println!("{:?}", vec);
}
Unhappily, this isn't quite the thing you want, since this will sort "Peter" before "charles" - the default comparator of strings is case-sensitive (in fact, it's even locale-agnostic, since it compares basing on Unicode code points). So, if you want to perform case-insensitive sorting, here's the modification:
fn main() {
let mut vec = Vec::new();
vec.push("richard");
vec.push("charles");
vec.push("Peter");
vec.sort_by(|a, b| a.to_lowercase().cmp(&b.to_lowercase()));
println!("{:?}", vec);
}

Resources