6

I'm new to Rust and looking to understand concepts like borrowing. I'm trying to create a simple two dimensional array using standard input. The code:

use std::io;

fn main() {
    let mut values = [["0"; 6]; 6]; // 6 * 6 array

    // iterate 6 times for user input
    for i in 0..6 {
        let mut outputs = String::new();
        io::stdin().read_line(&mut outputs).expect(
            "failed to read line",
        );

        // read space separated list 6 numbers. Eg: 5 7 8 4 3 9
        let values_itr = outputs.trim().split(' ');
        let mut j = 0;
        for (_, value) in values_itr.enumerate() {
            values[i][j] = value;
            j += 1;
        }
    }
}

This won't compile because the outputs variable lifetime is not long enough:

error[E0597]: `outputs` does not live long enough
  --> src/main.rs:20:5
   |
14 |         let values_itr = outputs.trim().split(' ');
   |                          ------- borrow occurs here
...
20 |     }
   |     ^ `outputs` dropped here while still borrowed
21 | }
   | - borrowed value needs to live until here

How can I get the iterated values out of the block into values array?

0

2 Answers 2

7

split() gives you substrings (string slices) borrowed from the original string, and the original string is outputs from line 6.

  1. The string slices can't outlive the scope of outputs: when a loop iteration ends, outputs is deallocated. Since values is longer lived, the slices can't be stored there.
  2. We can't borrow slices of outputs across a modification of outputs. So even if the String outputs itself was defined before values, we couldn't easily put the string slices from .split() into values; modifying the string (reading into it) invalidates the slices.

A solution needs to either

  • Use a nested array of String, and when you assign an element from the split iterator, make a String from the &str using .to_string(). I would recommend this solution. (However an array of String is not at as easy to work with, maybe already this requires using Vec instead.) 1
  • Read all input before constructing a nested array of &str that borrows from the input String. This is good if the nested array is something that you only need temporarily.

1: You can use something like vec![vec![String::new(); 6]; 6] instead

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

2 Comments

It would be ideal if I can solve this using arrays. But seems there is no way to create an array of Strings in Rust. Isn't it? First method works fine with the Vectors. Thanks for the answer.. (including your last remarks on IRC) "value is a &str, a substring from outputs. so .to_string() allocates space for a String and copies the substring into that. so value was a &str (a reference) but is turned into a String. since it's not a reference it's not restricted by the borrow checker or the life of outputs."
There's no nice way to create an array of strings. [[String::new(), String::new(), String::new(), String::new(), String::new(), String::new()], [String::new(), String::new(), String::new(), String::new(), String::new(), String::new()], ...] certainly isn't nice.
1

This answer was moved from the question, where it solved the OPs needs.

use std::io;

fn main() {
    let mut values = vec![vec![String::new(); 6]; 6];
    for i in 0..6 {
        let mut outputs = String::new();
        io::stdin().read_line(&mut outputs)
                .expect("failed to read line");

        let values_itr = outputs.trim().split(' ');
        let mut j = 0;
        for (_, value) in values_itr.enumerate() {
            values[i][j] = value.to_string();
            j += 1;
        }
    }
}

Comments

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.