Getting 2 integers from a same Vector of Chars

So I have a vector that has a many chars stored init, for now lets take the vector as ['1', '-', '1', '2']
Now what I want is for the left side of the vector (1) converted to an integer and the right side also converted to an integer but seperated like I want to have a variable for the left side and right side, But the thing is from the input or data I have the number of digits can be random ,
for example:
1-12
12-14
14-16
1-10
1-2

I want to split the right side from - and the left side from - so I can use those integers later.

Using a Vec<char> isn't a good idea - you should be using a String instead, it's a lot more space-efficient. With it you can do this:

let mut parts = s.splitn(2, '-');
let first: u32 = parts.next().unwrap().parse()?;
let second: u32 = parts.next()?.parse()?;
assert!(parts.next().is_none());

You will have to modify the ? parts for however you're handling errors, for example:

let mut parts = s.splitn(2, '-');
let first: u32 = parts.next().unwrap().parse().map_err(Error::InvalidFirstNumber)?;
let second: u32 = parts.next().ok_or(Error::NoDash)?.parse().map_err(Error::InvalidSecondNumber)?;
assert!(parts.next().is_none());
2 Likes

Thanks but I need to use a vector

Well, you can do v.iter().copied().collect() to copy all chars into String and use it as was stated above, while keeping the original vector intact. However, usage of Vec<char> is usually a rather strange requirement.

3 Likes

Vec also has a splitn method to split in half, but you'll need to do the parsing yourself

Hey I tried splitting them and tried to do this

let splitted_vec = be.splitn(2, '-');
            for i in splitted_vec {
                println!("{:?}",i);
            }

Got an error:

26 |             let splitted_vec = be.splitn(2, '-');
   |                                             ^^^ expected an `FnMut<(&char,)>` closure, found `char`
   |
   = help: the trait `for<'r> FnMut<(&'r char,)>` is not implemented for `char`

Vec::splitn currently doesn't support multiple types of pattern, but takes only a closure. Something like this should work:

let splitted_vec = be.splitn(2, |c| *c == '-');
2 Likes

Thanks for the quick reply.

Now what I want to do is i want to make two variables f1 which stores the first chars ot the 1st element of the Split and f2 which stores the other side of the vector or the 2nd element of the Split.

Then you'd use code similar to what I posted:

let mut parts = be.splitn(2, |&c| c == '-');
let first_part = parts.next().unwrap();
let second_part = parts
    .next()
    .ok_or_else(|| /* Error that occurs when there is no dash */)?;
assert!(parts.next().is_none());

But please explain to us why you are using a Vec<char> in the first place - in 99.9% of cases you don't want or need it.

2 Likes

Because thats how I processed my data from a txt.

I was solving advent of code day two and I copied their input to a txt and then I wrote my program something like this:

use std::fs;

fn main() {
    let file = (fs::read_to_string("src\\Data.txt").expect("Couldn't read file")).to_string();

    let split = file.lines();


    let nums = ['1', '2', '3', '4', '5', '6', '7', '8', '9'];

    let mut l = 0;

    for i in split {
        let word = i.chars().collect::<Vec<char>>();

        let mut be = Vec::new();
        for i in word {
            if i.is_numeric() {
                be.push(i)
            } else if i == '-' {
                be.push('-');
            } else if i.is_alphabetic() {
                continue
            }

            // let mut splitted_vec = be.splitn(2, |c| *c == '-');
            let mut parts = be.splitn(2, |&c| c == '-');
            let first_part = parts.next().unwrap();
            let second_part = parts
                .next();


            let mut  a = String::new();
            for i in first_part {
                let str = i.to_string();
                a += &*str;
            }
            let strr = a.parse::<i64>().unwrap();

            let mut  b = String::new();
            for pol in second_part {
                let str = pol.to_string();
                b+= &*str;
            }
            let s2 = b.parse::<i64>().unwrap();

            println!("{}-{}",strr, s2);


        }

    }
}

Make be a String then. String also has a method push that appends a char onto the end. Additionally you don't need to make word a Vec<char>: just iterate over its characters directly using for i in i.chars(). Let me reiterate: Vec<char> is never a good idea, try to avoid it if possible :slight_smile:

1 Like

LOL I just did that and no difference in code but I get error:

let str = pol.to_string();
   |                               ^^^^^^^^^ method not found in `&[char]`
   |
   = note: the method `to_string` exists but the following trait bounds were not satisfied:
           `[char]: std::fmt::Display`
           which is required by `[char]: ToString`
           `&[char]: std::fmt::Display`
           which is required by `&[char]: ToString`

You must still be creating a Vec<char> somewhere, otherwise there's no way to create an &[char].

Its from the second part variable now I don't know what to do, It has been a week and I am still stuck on processing the data for this problem maybe I should give up and come to this problem later.

I modified your program slightly just to get it to compile, here is a playground. I made the following changes:

  1. I replaced the loop for i in i.chars().collect::<Vec<char>>() with for i in i.chars(), since it's equivalent and avoids Vec<char>.
  2. I replaced let mut be = Vec::new() with let mut be = String::new() to avoid Vec<char>.
  3. I replaced .splitn(2, |&c| c == '-') with just .splin(2, '-') - for strings, splitn doesn't require a closure and you can simply pass in the character directly.
  4. I replaced let second_part = parts.next(); with let second_part = parts.next().unwrap(). In the code I posted earlier it used .ok_or_else plus the ? operator to handle the case where it was None, but since here you don't care about nice errors you can just panic if it's None via unwrap().

But now you're using String instead of Vec<char>, you can call parse directly on first_part and second_part instead of having to create a string! So, you can additionally replace:

let mut  a = String::new();
for i in first_part {
    let str = i.to_string();
    a += &*str;
}
let strr = a.parse::<i64>().unwrap();

with just:

let strr = first_part.parse::<i64>().unwrap();
3 Likes

thread 'main' panicked at 'called Option::unwrap() on a None value', src\main.rs:21:44
stack backtrace:

In my code line 21 doesn't even have any unwraps, can you show what is on line 21 for you?

let second_part = parts.next().unwrap();

Then the input must not have a dash in it as long as you don't call parts.next() more than twice. You can try inserting calls to the dbg! macro in your code to print out values.