So, to address your actual question. What your code currently tries to do is to use a vector of references:
// "" is a reference to a string slice, i.e. it has type &str
// thus names has type Vec<&str>
let mut names = vec![""; numberofnames];
By writing &name.trim()
, you create a reference to a sub-slice of the string name
, that string is however still going out of scope at the end of the loop body, and you cannot keep it around inside of names
for that reason. What you need here is a Vec<String>
. You could create one by writing vec![String::new(); numberofnames]
since String::new()
creates a new, empty string.
Now, you could make your code work like this
use std::io;
fn main() {
let mut numberofnames = String::new();
io::stdin()
.read_line(&mut numberofnames)
.expect("Need number of names");
let numberofnames: usize = numberofnames.trim().parse().expect("Need a number");
let mut names = vec![String::new(); numberofnames];
for x in 0..numberofnames {
let mut name = String::new();
io::stdin().read_line(&mut name).expect("Need name");
names[x] = name.trim().into();
}
}
.into()
is using the From
(and Into
) conversion trait to allocate a new String
containing a copy of a &str
slice.
There are a few things to improve here: There’s no easy way to avoid the need for allocating a new String
after the call to trim
. Hence name
does not need to be a new buffer every iteration, you could re-use the same one throughout your code, even from the beginning, like this:
use std::io;
fn main() {
let mut buffer = String::new();
io::stdin()
.read_line(&mut buffer)
.expect("Need number of names");
let numberofnames: usize = buffer.trim().parse().expect("Need a number");
buffer.clear();
let mut names = vec![String::new(); numberofnames];
for x in 0..numberofnames {
io::stdin().read_line(&mut buffer).expect("Need name");
names[x] = buffer.trim().into();
buffer.clear();
}
}
Since the names
vector already contains Strings
you can just append to them, this would however (I think) not really give much or any performance gain, just a point I wanted to make: You can change the names[x] = ...
line to
names[x].push_str(buffer.trim());
You could also avoid pre-filling the vec with String::new()
s, and instead just create an empty Vec
with an initial capacity:
use std::io;
fn main() {
let mut buffer = String::new();
io::stdin()
.read_line(&mut buffer)
.expect("Need number of names");
let numberofnames: usize = buffer.trim().parse().expect("Need a number");
buffer.clear();
let mut names = Vec::with_capacity(numberofnames);
for _ in 0..numberofnames {
io::stdin().read_line(&mut buffer).expect("Need name");
names.push(buffer.trim().to_string());
buffer.clear();
}
}
Another useful thing to do is to avoid (implicitly) re-locking stdin
for every line when you’re the only user of it anyways:
use std::io;
use io::BufRead; // necessary to have `.read_line()` on
// locked stdin available
fn main() {
let stdin = io::stdin();
let mut locked_stdin = stdin.lock();
let mut buffer = String::new();
locked_stdin
.read_line(&mut buffer)
.expect("Need number of names");
let numberofnames: usize = buffer.trim().parse().expect("Need a number");
buffer.clear();
let mut names = Vec::with_capacity(numberofnames);
for _ in 0..numberofnames {
locked_stdin.read_line(&mut buffer).expect("Need name");
names.push(buffer.trim().to_string());
buffer.clear();
}
}
Also, for convenienve, but going back to the slightly worse approach of using a new buffer for each line (so more allocations), you could use the .lines()
iterator from BufRead
:
use io::BufRead;
use std::io; // necessary to have `.lines()` on
// locked stdin available
fn main() {
let stdin = io::stdin();
let mut lines = stdin.lock().lines();
let numberofnames: usize = lines
.next()
.expect("Need input from stdin") // checking for any line being present
.expect("Need number of names")
.trim()
.parse()
.expect("Need a number");
let names = lines
.take(numberofnames)
.map(|line| line.expect("Need name").trim().into())
.collect::<Vec<String>>();
if names.len() < numberofnames {
panic!("Need more names, only got {}!", names.len());
}
}