Creating vec! from another one with same changes to all elements

I've this vector:

let current_status: Vec<&str> = vec!["happy", "excited","glad"];

And need to change it to:

let status_reg: Vec<&str> = vec!["r\"happy\"i", "r\"excited\"i", "r\"glad\"i"];

i.e. I need to change each element value with this format

format!("r\"{}\"i", status);

I tried the below:

let mut status_reg: Vec<&str> = vec![Default::default()];
for status in rcurrent_status {
    status_reg.push(format!("r\"{}\"i", status).as_str()); 
}

But got the below error:

    status_reg.push(format!("r\"{}\"i", status).as_str()); 
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
    status_reg.push(format!("r\"{}\"i", status).as_str()); 
                                                         ^ temporary value is freed at the end of this statement
    status_reg.push(format!("r\"{}\"i", status).as_str()); 
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that borrow lasts for `'static`
temporary value dropped while borrowed
current_status.iter().map(|s| format!("r\"{}\"i", s)).collect::<Vec<_>>();

You need to create owned String values (what format! does), and collect them into a Vec.

Why do you need to end up with Vec<&str> rather than Vec<String>?

2 Likes

Think about the type &str in both of the Vecs. The & means that it's a reference.

In current_status, the references are to strs that are compiled into your executable, but to where do the references in rcurrent_status point? The answer is that the references point to heap-allocated Strings. But those strings get dropped too soon:

let mut status_reg: Vec<&str> = vec![Default::default()];
for status in rcurrent_status {
    status_reg.push({
        let heap_allocated: String = format!("r\"{}\"i", status);
        let str_reference: &str = heap_allocated.as_str();
        str_reference
        // heap_allocated dropped here.  What would str_reference point to?
    }); 
}

The solution is to make a vector of heap-allocated Strings as vitalyd suggests. If needed, you can then make a vector that references the vector of heap-allocated Strings.

2 Likes

Because I've the following lines as:

let set = RegexSet::new(&status_reg).unwrap();

let mut file = Docx::open(&path).expect("Cannot open file");
let mut isi = String::new();
let _ = file.read_to_string(&mut isi);

let matches: Vec<_> = set.matches(&isi).into_iter().collect();
let exact_matches = matches.iter().
                                    map(|s| status_reg[*s]).collect::<Vec<&str>>();

and having String will case:

   |
44 |                           map(|s| status_reg[*s]).collect::<Vec<_>>();
   |                                     ^^^^^^^^^^^^^^ move occurs because value has type `std::string::String`, which does not implement the `Copy` trait

Not sure if I can re-write it to avoid &str

RegexSet::new can work off any iterator whose elements are AsRef<str>; a Vec<String> or &[String] fits that bill. So if status_reg is a Vec<String>, RegexSet::new(&status_reg) should work. Or did I misunderstand the problem?

mmm, well, below is the full code:

use std::io::Read;

use dotext::*;
use regex::RegexSet;

fn main(){
    let required_skills: Vec<&str> = vec![
        "Project(s|^s)",  // (s|^s) ending with or without `s`
        "Plicy|Policies",  // either singular or plural
    ]; //  to be scanned in the given CV

    let searches =required_skills.iter().map(|s| *s).
        collect::<Vec<&str>>();

// I need to replace the above by the below?!
//    let searches =required_skills.iter().map(|s| format!("r\"{}\"i", s)).
//        collect::<Vec<_>>();

// So based on your answer, I re-wrote it as:
//    let skills_regex =required_skills.iter().map(|s| format!("r\"{}\"i", s)).
//        collect::<Vec<_>>();

//    let searches = skills_regex.iter().map(|s| *s).
//        collect::<Vec<&str>>();

    let set = RegexSet::new(&searches).unwrap();  

    let mut file = Docx::open("CV.docx").expect("Cannot open file");
    let mut isi = String::new();
    let _ = file.read_to_string(&mut isi);

    if set.is_match(&isi){
        let matches = set.matches(&isi).into_iter().collect::<Vec<_>>();
        let exact_matches = matches.iter().
                                    map(|s| searches[*s]).collect::<Vec<&str>>();
        println!("Out of {} requirements, the below {} matches had been found:",
                set.len(), matches.len());
        for x in exact_matches {
                println!("{}", x );
        }
    } else {
            println!("No matches found");
    }
}

In the “rewrite”, you don’t need searches - just use skills_regex. That’ll be a Vec<String>. As long as you keep it alive longer than your loop, which you do, you should be able to look things up in it by index and get a &str to an element.

mm, so, with

   let searches =required_skills.iter().map(|s| format!("r\"{}\"i", s)).
        collect::<Vec<_>>();

If I wrote:

let exact_matches = matches.iter().map(|s| searches[*s]).collect::<Vec<_>>();

I get

  --> src/main.rs:40:45
   |
40 |                                     map(|s| searches[s]).collect::<Vec<_>>();
   |                                             ^^^^^^^^^^^ slice indices are of type `usize` or ranges of `usize`
   |
   = help: the trait `std::slice::SliceIndex<[std::string::String]>` is not implemented for `&usize`

If I wrote:

let exact_matches = matches.iter().map(|s| searches[&s]).collect::<Vec<_>>();

I get

  --> src/main.rs:40:45
   |
40 |                                     map(|s| searches[&s]).collect::<Vec<_>>();
   |                                             ^^^^^^^^^^^^ slice indices are of type `usize` or ranges of `usize`
   |
   = help: the trait `std::slice::SliceIndex<[std::string::String]>` is not implemented for `&&usize`
   = note: required because of the requirements on the impl of `std::ops::Index<&&usize>` for `std::vec::Vec<std::string::String>`

If I wrote:

let exact_matches = matches.iter().map(|s| searches[s]).collect::<Vec<_>>();

I get

--> src/main.rs:40:45
|
40 | map(|s| searches[s]).collect::<Vec<_>>();
| ^^^^^^^^^^^ slice indices are of type usize or ranges of usize
|
= help: the trait std::slice::SliceIndex<[std::string::String]> is not implemented for &usize
= note: required because of the requirements on the impl of std::ops::Index<&usize> for std::vec::Vec<std::string::String>

Try:

let exact_matches = matches.iter().map(|&s| searches[*s]).collect::<Vec<_>>();

That is, you need to get a usize for indexing, not a ref to one.

Can also do matches.iter().cloned().map(|s| searches[s])...

I got:

error[E0614]: type `usize` cannot be dereferenced
  --> src/main.rs:42:62
   |
42 |         let exact_matches = matches.iter().map(|&s| searches[*s]).collect::<Vec<_>>();
   |                                                              ^^

error: aborting due to previous error

I got:

error[E0507]: cannot move out of index of `std::vec::Vec<std::string::String>`
  --> src/main.rs:43:61
   |
43 |         let exact_matches = matches.iter().cloned().map(|s| searches[s]).collect::<Vec<_>>();
   |                                                             ^^^^^^^^^^^ move occurs because value has type `std::string::String`, which does not implement the `Copy` trait

error: aborting due to previous error

Here's a working example with minimal changes (playground):

use std::io::Read;

use regex::RegexSet;

fn main(){
    let required_skills: Vec<&str> = vec![
        "Project(s|^s)",  // (s|^s) ending with or without `s`
        "Plicy|Policies",  // either singular or plural
    ]; //  to be scanned in the given CV

    let searches =required_skills.iter().map(|s| format!("r\"{}\"i", s)).
        collect::<Vec<String>>();

    let set = RegexSet::new(&searches).unwrap();  

    let mut file = std::fs::File::open("CV.docx").expect("Cannot open file");
    let mut isi = String::new();
    let _ = file.read_to_string(&mut isi);

    if set.is_match(&isi){
        let matches = set.matches(&isi).into_iter().collect::<Vec<_>>();
        let exact_matches = matches.iter().
                                    map(|s| searches[*s].as_str()).collect::<Vec<_>>();
        println!("Out of {} requirements, the below {} matches had been found:",
                set.len(), matches.len());
        for x in exact_matches {
                println!("{}", x );
        }
    } else {
            println!("No matches found");
    }
}

(Note: I changed Docx::open to File::open just to make it compile on the playground.)

Oh sorry, I thought s was &&usize by mistake.

Anyway, you need to take a reference out of the index operation, either via as_ref as @mbrubeck said or adding an ampersand: &searches[*s]

I’m on my phone so a bit sloppy, sorry

1 Like

mmm, it compiled without any error, but gave "No matches found" while there is a match, may I've to look again into the regex format: r\"{}\"i

It is ok, deeply appreciate your support

1 Like

I found the correct format to ignore case sensitivity to be:

format!("(?i){}", s)