mjwrazor
mjwrazor

Reputation: 1994

How to convert a String of strings to an Array/Vector of Strings in Rust

I have a string from a CSV file that contains multiple lines:

Edit. Rust was not in the actual string.

header1,header2,header3
r1v1,r1v2,r1v3
r2v1,r2v2,r2v3

I am trying to push these into a Vec<Vec<String>>:

// the csv is raw_string
let lines = raw_string.lines();
let mut mainVec = Vec::new();

for row in lines {
    let mut childVec = Vec::new();
    let trimmed = row.trim();
    let values = trimmed.split(',').collect();
    childVec.push(values);
    mainVec.push(childVec.clone());
}

But I get the error

error[E0282]: unable to infer enough type information about `_`
 --> src/main.rs:9:13
  |
9 |         let values = trimmed.split(',').collect();
  |             ^^^^^^ cannot infer type for `_`
  |
  = note: type annotations or generic parameter binding required

Upvotes: 1

Views: 6640

Answers (2)

VP.
VP.

Reputation: 16765

Another solution, based on iterators:

fn main() {
    let raw_string = r"rust
header1,header2,header3
r1v1,r1v2,r1v3
r2v1,r2v2,r2v3";

    let main_vec = raw_string.lines()
                             .map(|s| s.trim().split(',').map(String::from).collect::<Vec<String>>())
                             .collect::<Vec<Vec<String>>>();

    print!("{:?}", main_vec);
}

As @Simon Whitehead has already said, the the only thing you need to do with collect() is to specify the type because this is a generic method. But it may also be deduced by the compiler itself in some circumstances.

The code above is pretty verbose about type specification: actually you may leave the Vec's value type unspecified and let it be deduced by the compiler like this:

let main_vec = raw_string.lines()
                         .map(|s| s.trim().split(',').map(String::from).collect::<Vec<_>>())
                         .collect::<Vec<_>>();

For more information, see the definition of collect() method.

Upvotes: 5

Simon Whitehead
Simon Whitehead

Reputation: 65087

You need to give the compiler a little hint as to what you want values to be.

You can say you want it to be a vector of something:

let values: Vec<_> = trimmed.split(',').collect();

Working example in the playground

A few other notes about the code, if you're interested.

  • The variable names should be snake_case. So the vectors should be called main_vec and child_vec.
  • The child_vec does not need to be cloned when pushing it to the main_vec. Pushing it to main_vec transfers ownership and the loop redeclares the child_vec and so the compiler can guarantee nothing has been violated here.

Upvotes: 3

Related Questions