[SOLVED] Mutable borrow and lifetime issues

Hi all,

I'm trying to implement a trait (Strip), that can delete chars from a &mut String based on a Pattern.

I don't understand why my code compiles when the pattern is defined inside my trait method (example 1). An why it fails when my pattern is passed as a parameter of my trait method (example 2).

The compiler gives me the following error :


error[E0502]: cannot borrow `*data` as mutable because it is also borrowed as immutable
  --> src/main.rs:18:13
   |
15 |     fn strip<P: Pattern<'a> + Copy>(&self, pattern: P, data: &'a mut String) { 
   |                         -- lifetime `'a` defined here
16 |         while let Some(index) = data.find(pattern) {
   |                                 ------------------
   |                                 |
   |                                 immutable borrow occurs here
   |                                 argument requires that `*data` is borrowed for `'a`
17 |             // The following line triggers a compile error
18 |             data.remove(index);
   |             ^^^^^^^^^^^^^^^^^^ mutable borrow occurs here

For more information about this error, try `rustc --explain E0502`.

There's some lifetime issues that I don't understand well and that I don't know how to fix.

example 1:

#![feature(pattern)]
#![feature(in_band_lifetimes)]

use std::str::pattern::Pattern;

pub struct Foo {}

pub trait Strip {
    fn strip<P: Pattern<'a> + Copy>(&self, pattern: P, data: &'a mut String); 
}

impl Strip for Foo
{
    // In this example I don't use the _pattern parameter
    fn strip<P: Pattern<'a> + Copy>(&self, _pattern: P, data: &'a mut String) { 
        while let Some(index) = data.find(char::is_lowercase) {
            data.remove(index);
        }
    }
}

fn main() {
    let foo = Foo{};
    let mut input = "AbCdEf".to_string();
    
    foo.strip(char::is_lowercase, &mut input);
}

example 2:

#![feature(pattern)]
#![feature(in_band_lifetimes)]

use std::str::pattern::Pattern;

pub struct Foo {}

pub trait Strip {
    fn strip<P: Pattern<'a> + Copy>(&self, pattern: P, data: &'a mut String); 
}

impl Strip for Foo
{
    // In this example I'm using the pattern parameter
    fn strip<P: Pattern<'a> + Copy>(&self, pattern: P, data: &'a mut String) { 
        while let Some(index) = data.find(pattern) {
            // The following line triggers a compile error
            data.remove(index);
        }
    }
}

fn main() {
    let foo = Foo{};
    let mut input = "AbCdEf".to_string();
    
    foo.strip(char::is_lowercase, &mut input);
    foo.strip(' ', &mut input);
}

Thank you for you help.

You don't want to tie the lifetime of the Pattern to the lifetime of the String, that attempts to borrow it for too long. The lifetime of the pattern is the lifetime the string being searched in should be borrowed for, which in this case doesn't need to be longer than the expression in the while let. What you need to do to fix it is specify that the pattern can work for any lifetime with for<'a> Pattern<'a>, and elide the separate lifetime on the string.

Corrected version of example 1

Corrected version of example 2

Note that I've also removed in_band_lifetimes since it isn't necessary here and tends to confuse things.

2 Likes

Thank you @jameseb7, for your clear and concise answer!

I didn't know the for<'a> annotation. It's make more sens now

The for<'a> syntax isn't that common, but if you want to read a bit more on it, it's called a Higher-Ranked Trait Bound (HRTB), and is discussed here in the nomicon and here in the reference. It doesn't get much discussion in the book except for a brief mention in the appendices of keywords and operators, where it isn't really explained what a HRTB is.

1 Like

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.