Help with capture mutable reference

I don't understand why buf cannot be borrow as mut in the closure, would somebody care to explain

fn get_words<'a>(s: &'a str) -> impl Iterator<Item = (usize, usize, String)> + 'a {
    let mut start = 0;
    let mut end = 0;
    let word = String::new();
    let buf = &word;
    s.chars().enumerate().filter_map(move |(i, c)| {
        if c.is_whitespace() {
            end = i;
            let result = (start, end, buf.to_string());
            start = i + 1;
            end = 0;
            Some(result)
        } else {
            buf.push(c);
            None
        }
    })
}

Two problems. First, buf is not a mutable reference &mut String, but an immutable one, &String, since you used the & operator instead of the &mut operator: you would have to write let buf = &mut word. Then if you fix that, you will get a compile error saying that word was not declared as mutable (let mut word).

However, there's no need for any of that, because word is never used at all, so there's no need for a mutable reference; just declare a mutable String that you then use in the closure; replace the two lines declaring word and buf.

    let mut buf = String::new();

After fixing that, you will get warnings that the “value captured by end is never read”, which is true — you assign end = i; immediately before the only place you read end. It looks like there is simply no need at all for end to be a captured variable.

Also, I ran the code and discovered that buf isn't cleared after producing each word, which is probably not what you want, so I fixed that using std::mem::take (buf.clear() would have worked fine too, but this saves copying the string) and got this working code:

use std::mem;

fn get_words<'a>(s: &'a str) -> impl Iterator<Item = (usize, usize, String)> + 'a {
    let mut start = 0;
    let mut buf = String::new();
    s.chars().enumerate().filter_map(move |(i, c)| {
        if c.is_whitespace() {
            let end = i;
            let result = (start, end, mem::take(&mut buf));
            start = i + 1;
            Some(result)
        } else {
            buf.push(c);
            None
        }
    })
}

fn main() {
    for word in get_words("one two three four") {
        println!("{:?}", word);
    }
}
2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.