简体   繁体   中英

Cannot infer lifetime for autoref when calling mutable method from closure

Here's a playground link that reproduces the error: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=86ec4f11f407f5d04a8653cc904f991b

I have a trait FooTraitMut that provides access to a specific range of data inside of BarStruct , and I want to generalize this trait so that it can access the same range on multiple BarStruct s in lockstep. So I have a MutChannels trait that acts like a type-level function to produce the tuple of references that a visitor needs, eg (T, U) --> (&mut T, &mut U) .

I haven't actually gotten to the point of using Channels2 because I can't get the simpler Channels1 case to work.

In the playground, the same is done for an immutable trait FooTraitRef , which works as intended. But the mutable one is broken due to an autoref lifetime issue. I think some kind of implicit transformation is happening to the lifetime of self , because I can inline the indexer function and it works fine.

Any help would be greatly appreciated.

The code in question:

struct BarStruct<T> {
    data: [T; 1],
}

pub struct Channels1<T>(T);
pub struct Channels2<T, U>(T, U);

fn indexer(mut f: impl FnMut(usize)) {
    f(0)
}

trait FooMutTrait {
    type Data: for<'a> MutChannels<'a>;

    fn foo<'a, F>(&'a mut self, f: F)
    where
        F: FnMut(<Self::Data as MutChannels<'a>>::Mut);
}

trait MutChannels<'a> {
    type Mut;
}

impl<'a, T: 'a> MutChannels<'a> for Channels1<T> {
    type Mut = &'a mut T;
}
impl<'a, T: 'a, U: 'a> MutChannels<'a> for Channels2<T, U> {
    type Mut = (&'a mut T, &'a mut U);
}

impl<T> BarStruct<T> {
    fn get_data_mut<'a>(&'a mut self, i: usize) -> &'a mut T {
        &mut self.data[i]
    }
}

impl<T> FooMutTrait for BarStruct<T>
where
    T: 'static,
{
    type Data = Channels1<T>;

    #[inline]
    fn foo<'a, F>(&'a mut self, mut f: F)
    where
        F: FnMut(<Self::Data as MutChannels<'a>>::Mut),
    {
        indexer(|i| f(self.get_data_mut(i)))
        
        // This works.
        // f(self.get_data_mut(0))
    }
}

And the error:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/lib.rs:85:28
   |
85 |         indexer(|i| f(self.get_data_mut(i)))
   |                            ^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'_` as defined on the body at 85:17...
  --> src/lib.rs:85:17
   |
85 |         indexer(|i| f(self.get_data_mut(i)))
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that closure can access `self`
  --> src/lib.rs:85:23
   |
85 |         indexer(|i| f(self.get_data_mut(i)))
   |                       ^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the method body at 81:12...
  --> src/lib.rs:81:12
   |
81 |     fn foo<'a, F>(&'a mut self, mut f: F)
   |            ^^
note: ...so that reference does not outlive borrowed content
  --> src/lib.rs:85:23
   |
85 |         indexer(|i| f(self.get_data_mut(i)))
   |                       ^^^^^^^^^^^^^^^^^^^^

This error can be reproduced by this example:

fn indexer(mut f: impl FnMut()) {}

fn foo<'a, F>(a: &'a mut String, mut f: F)
where
    F: FnMut(&'a mut str),
{
    indexer(|| f(a.as_mut_str()));
}
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
 --> src/lib.rs:7:20
  |
7 |     indexer(|| f(a.as_mut_str()));
  |                    ^^^^^^^^^^
  |
note: first, the lifetime cannot outlive the lifetime `'_` as defined on the body at 7:13...
 --> src/lib.rs:7:13
  |
7 |     indexer(|| f(a.as_mut_str()));
  |             ^^^^^^^^^^^^^^^^^^^^
note: ...so that closure can access `a`
 --> src/lib.rs:7:18
  |
7 |     indexer(|| f(a.as_mut_str()));
  |                  ^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the function body at 3:8...
 --> src/lib.rs:3:8
  |
3 | fn foo<'a, F>(a: &'a mut String, mut f: F)
  |        ^^
note: ...so that reference does not outlive borrowed content
 --> src/lib.rs:7:18
  |
7 |     indexer(|| f(a.as_mut_str()));
  |                  ^^^^^^^^^^^^^^

What is happening is that F is typed to expect an 'a reference, but that's not what the closure can provide. The closure converts the &'a mut T into a shorter lifetime &'_ mut T . From my understanding, it would be unsound for FnMut s to preserve the outer lifetime since the function would be able to leak references outside its scope and could violate Rust's referential guarantees. This problem doesn't come up with immutable borrows since they are less restricted and the closure doesn't shorten their lifetime.

This can be fixed by allowing F to work for any lifetime:

fn indexer(mut f: impl FnMut()) {}

fn foo<'a, F>(a: &'a mut String, mut f: F)
where
    F: FnMut(&mut str), // <--------
{
    indexer(|| f(a.as_mut_str()));
}

Or using FnOnce since its functionality more constrained and doesn't require shortening the lifetimes:

fn indexer(f: impl FnOnce()) {} // <--------

fn foo<'a, F>(a: &'a mut String, mut f: F)
where
    F: FnOnce(&'a mut str), // <--------
{
    indexer(move || f(a.as_mut_str())); // added move so that the reference isn't reborrowed
}

The FnOnce change is trivial to apply to your case. However, relaxing F to work for any lifetime is met with errors about MutChannels<'_>::Mut not being the same as &'_ mut T , which I'm not sure how to handle.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM