0

I'm new to rust and I'm having a hard time wrapping my head around ownership concepts. I have a string that has substrings with a specific pattern inside of it, like "alice [bob] charlie [dave]", and I'm trying to get to a vector of strings that looks like ("john","dave"). My first go was at using a regex with non capturing groups, but I can't get that to work, because I don't really understand rust very well:

let re = Regex::new(r"(?:\[)([a-zA-Z]+)(?:])").unwrap();
let mut answer: Vec<&str> = re.captures_iter((&text)).map(|capGroup| &*String::from(&capGroup[1])).collect();

But I could collect them like so:

let re = Regex::new(r"\[[a-zA-Z0-9_]+]").unwrap();
let mut answer: Vec<&str> = re.find_iter((&text)).map(|mtc| mtc.as_str()).collect();

The problem with this, is that it leaves me with ("[bob]","[dave]"), and I've also been unsucessuful at trimming the first and last few characters of the string. As a matter of fact, I don't even understand how iterating works here it seems, because when I try to do:

 let mut it = answer.iter();
 for name in &mut it{
      ...

I end up with name being of type &&str instead of the &str that I expected. So my questions would be:

  • How do I use capture groups in rust for what I'm trying to do in the first example, in an idiomatic fashion? I feel like I'm close to the mark of applying something to all the captures and collecting into a vec but something is escaping me.
  • Given Vec<&str>, how do I modify the strings inside it? What is it that I'm missing here?
  • Why is my iteration item of type &&str when I do that?
  • Is there a better way overall of achieving this in rust?
Peter Hall
  • 53,120
  • 14
  • 139
  • 204

2 Answers2

1

To remove the "[]", you can add a capturing group in your regular expression and then filter only the matches that are captured by it:

let text = "alice bob [charlie] dave [emily]";

let re = Regex::new(r"\[([a-zA-Z0-9_]+)]").unwrap();
let answer: Vec<&str> = re
    .captures_iter(text)
    .filter_map(|capture| capture.get(1))
    .map(|mtch| mtch.as_str())
    .collect();

println!("{:?}", answer); // ["charlie", "emily"]

Capture::get returns an Option so you can use that with filter_map to filter out all of the matches that aren't captured there, since capture.get(1) will return None for those.


To answer the other question, the reason why the items in the iterator are of type &&str is because the vector contains &str, but you are iterating over a reference to the vector so everything is doubly borrowed.

These borrow the vector so you are iterating over &&str:

for item in &answer {
    // item is &&str
}

// which is syntactic sugar for:
for item in (&answer).into_iter() {
    // item is &&str
}

for item in answer.iter() {
    // item is &&str
}

This iterates over &str, but moves the vector so you can't use it again:

for item in answer {
    // item is &str
}

// is sugar for:
for item in answer.into_iter() {
    // item is &str
}

You can iterate over &str without moving the vector by copying the slices. This is cheap because only the pointer is copied:

for item in answer.iter().copied() {
    // item is &str
}

Having &&str instead of &str is almost never a problem. Methods of &str can still be called on a &&str and you can always just dereference it with * if you need to.

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
1

Just some general information: In rust &str is just a reference to a string slice, meaning there is no ownership of the pointing variable. So in your example let mut answer: Vec<&str> the mutable variable answer doesn't own any of the slices in the Vec. Since the &str 's are non-mutable str references,you would need ownership in order to mutate them. So you could convert them into a String object with the .to_owned() function. String slices(&str) and Strings(String) are completely different in rust. Here is a good explanation: What are the differences between Rust's `String` and `str`?.

  1. For your question "Why is my iteration item of type &&str when I do that?", you are referencing(&) a reference to a string slice(&str).

  2. For your question "Given Vec<&str>, how do I modify the strings inside it? What is it that I'm missing here?" a way to go about this is to use the String struct.

RonsJurisdiction
  • 240
  • 1
  • 2
  • 15