Vec and split error

Problem with Vec and split.

I have a Vec (I think) of substrings 'BWK, COB...' named 'arg_club' created from

let mut arg_club = arg_two.to_string().split("/");

where arg_two is a string 'BWK/COB/ESS' as the program CLI parameter 2.

I wish to pass the 'arg_club' as 'in_clubs' to a function:

pub fn process_line(in_line : String, in_date : i32, in_clubs : Vec) -> (i32, String)

The compiler complains with the following error when the function is used:

let (zone, err_string) = funcline::process_line(line, local_year, arg_club);
^^^^^^^^ expected struct std::vec::Vec, found struct 'std::str::Split`

I am learning but cant work out how to convert the string to some type of Vec so I can pass it to the function.

Any help appreciated, Ian.

Calling split returns an iterator over the segments of the string. Iterators are lazy (i.e. they'll only do the work to get the next element of the iterator when they're asked for it), so if you want to turn them into a full collection, you need to use collect, which I think would look something like this:

let mut arg_club: Vec<&str> = arg_two.to_string().split("/").collect();
// or
let mut arg_club = arg_two.to_string().split("/").collect::<Vec<&str>>();

I will try to answer directly to your question, then I will comment some points.

str::Split is an Iterator, so you need to collect from it into a Vec (in this case). You also did not specify if your function takes a Vec<String> or a Vec<&str>. str::Split::collect produces str (a sized reference to a contiguous UTF-8 encoded bytes), and if you need the former then you have to do something like

let vec_club: Vec<_> = arg_club.map(|s| s.to_string()).collect();

and you can pass this to the function.

See if this already helps. Now I want to show you a couple of things.

It looks like arg_two is a str, are you sure you need to create an owned String with to_string()?

If you have the possibility, it is probably better to change the signature of process_line in order to take strs and slices of str (or, even better in your case, an Iterator). Here what I mean using slices and strs:

use std::env;

fn main() {
    let arg_two = env::args()
        .skip(2)
        .next()
        .expect("I need at least two arguments!");
    let arg_club: Vec<_> = arg_two.split("/").collect();
    let _ = process_line(&arg_two, 0, &arg_club);
}


pub fn process_line<'a>(
    in_line: &'a str,
    in_date: i32,
    in_clubs: &[&'a str],
) -> (i32, &'a str) {
    // Just for example
    (in_date, in_clubs[2])
}

In this case I told the compiler that in_line, the strs in the slice in_clubs and the str you are getting as output have the same lifetime.

An interesting approach in this case could also be something like

use std::env;

fn main() {
    let arg_two = env::args()
        .skip(2)
        .next()
        .expect("I need at least two arguments!");
    let _ = process_line(&arg_two, 0, arg_two.split("/"));
}

pub fn process_line<'a, Iter>(
    in_line: &'a str,
    in_date: i32,
    in_clubs: Iter,
) -> (i32, &'a str)
where
    Iter: Iterator<Item = &'a str>
{
    // Just for example, don't do that!
    (in_date, in_clubs.skip(2).next().unwrap())
}

In this case the function is taking an object implementing Iterator and that returns a reference to str with the same lifetime of in_line.

Maybe your case can be different from what I showed you, but on the other hand I hope these examples can be useful.

2 Likes

Gentlefolk,
Thank you for your very informative responses.

I have applied the suggestion/s of 17cupsofcoffee and that has resolved the miss match on types. Will have a look at the instructions supplied by dodomorandi when I address the following....

The compiler now reports numerous errors about 'move' and things not 'living' long enough, etc....
Looks like I have some rust program structural/organizational issues....

I battle on, thanks for the help, Ian.

:+1: Keep on trying and don't be discuraged.

From what you are saying, maybe you did not read the Rust book. For this specific case, give a look to to the references and borrowing part, you will find it helpful (probably it is hard to understand what I said in my last post without this read).

If you have time and you are hungry of knowledge, my advice is to read to book, from the beginning to the end.You will find it enlightening. Another precious resource can be Rust by example, but you generally need some knowledge from the book to fully understand what is going on.

And just a final note: don't fight the compiler, it's your friend :smile:

Thank you once again.
I am aging hardware/software/network engineer who has occasionally programmed in machine and assembly language in the bowels of huge systems. I have also done a lot of programming in procedural languages such Pascal and Erlang to extract and process log files and statistics.

To keep the mind working I have been working on something from a totally different paradigm, Rust. Reading the book, working some examples, looking at example code. Learning.....

However something like the following bends my mind....... A value dropped while still borrowed in one statement....

Regards, ian.

let mut arg_club  = arg_two.to_string().split("/").collect::<Vec<&str>>();
   |                         ^^^^^^^^^^^^^^^^^^^                                  - temporary value dropped here while still borrowed
   |                         |
   |                         temporary value does not live long enough
...
64 | }
   | - temporary value needs to live until here
1 Like

Ok, I feel I bit guilty about that because I noticed the problem but I forgot to explain something about it.

To fully understand what is going on, you need to understand the signature of str::split():

pub fn split<'a, P>(&'a self, pat: P) -> Split<'a, P> where
    P: Pattern<'a>, 	[src]

This means that you take a reference to the current object, and returns back a Split object with the same lifetime. However, with to_string() you are creating an object that is not owned by any variable. Therefore, arg_club is a Vec of &str pointing to a String that have been dropped -- in C/C++ you would have obtained a beautiful undefined behaviour. The Rust compiler helps you avoiding these kind of errors.