How to collect string of input into array?

Hi All,

I am Rust noob and please bear with me if this question is already answered.

I want to read list of names from console input and store them in a names array.

Blockquote
use std::io;
fn main(){
let mut numberofnames = String::new();
io::stdin()
.read_line(&mut numberofnames)
.expect("Need number of names");
let numberofnames: uszie = numberofnames.trim().parse().expect("Need a number");
let mut names = vec![""; numberofnames];
for x in 0..numberofnames {
let mut name = String::new();
io::stdin().read_line(&mut word).expect("Need name");
names = &name.trim();
}
}

Blockquote
I get an error on line 12 that the 'name' borrowed value does not live long enough. It make sense as it will be descoped after the for loop. Not sure how you create a string and put that into an array. Any help is appriciated.

Hi and welcome to this forum!

How is everyone overlooking the pinned post on syntax highlighting...

Anyways, this is what your code actually looks like when properly formatted (including fixes for the typos):

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![""; numberofnames];
    for x in 0..numberofnames {
        let mut name = String::new();
        io::stdin().read_line(&mut name).expect("Need name");
        names[x] = &name.trim();
    }
}

Error message:

   Compiling playground v0.0.1 (/playground)
error[E0597]: `name` does not live long enough
  --> src/main.rs:12:21
   |
12 |         names[x] = &name.trim();
   |         -----       ^^^^ borrowed value does not live long enough
   |         |
   |         borrow later used here
13 |     }
   |     - `name` dropped here while still borrowed
2 Likes

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());
    }
}
2 Likes

:bulb: TIL that you can write

let mut names = vec![String::new(); numberofnames];

It doesn't have to be Copy, it only has to be Clone. From Macro std::vec:

Note that unlike array expressions this syntax supports all elements which implement Clone and the number of elements doesn't have to be a constant.

This will use clone to duplicate an expression, so one should be careful using this with types having a nonstandard Clone implementation. For example, vec![Rc::new(1); 5] will create a vector of five references to the same boxed integer value, not five references pointing to independently boxed integers.

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.