为什么在使用嵌套的可变引用时,我会收到错误“无法推断泛型参数的适当生命周期”?

时间:2018-04-19 19:29:06

标签: rust

在编写习惯使用Rust时,我偶然发现编译错误。我想了解为什么我会收到错误以及该怎么做:

  

无法推断泛型中的生命周期参数的适当生命周期   因需求冲突而输入

我一直在研究很多涉及类似错误的问题,但大多数似乎与循环依赖有关,我不认为这就是这里发生的事情。

这是我对MWE的尝试,仍然可以进一步减少:

Playground link(稍有不同的错误讯息)

pub struct InnerMut<T> {
    state: u32,
    stored_fn: fn(&mut T, u32),
}

impl<T> InnerMut<T> {
    pub fn new(stored_fn: fn(&mut T, u32)) -> InnerMut<T> {
        return InnerMut {
            state: std::u32::MAX,
            stored_fn,
        };
    }
    pub fn mutate(&mut self, data: &mut T) {
        (self.stored_fn)(data, self.state);
        self.state -= 1;
    }
}

pub struct StoreFnMut<F>
where
    F: FnMut(&mut [u8]),
{
    mutable_closure: F,
}

impl<F> StoreFnMut<F>
where
    F: FnMut(&mut [u8]),
{
    pub fn new(mutable_closure: F) -> StoreFnMut<F> {
        StoreFnMut { mutable_closure }
    }
    fn run_closure_on_mutable_borrow(&mut self) {
        let mut buf = vec![0; 100];
        (self.mutable_closure)(&mut buf[..]);
    }
}

fn foo(borrow: &mut &mut [u8], val: u32) {
    borrow[0] = (val & 0xff) as u8;
}

fn main() {
    let mut capturing_closure;
    let mut store_fn_mut;
    let mut inner_mut;

    inner_mut = InnerMut::new(foo);
    capturing_closure = move |mut borrow: &mut [u8]| {
        inner_mut.mutate(&mut borrow);
    };
    store_fn_mut = StoreFnMut::new(capturing_closure);
    store_fn_mut.run_closure_on_mutable_borrow();
}

使用Rust 1.24.1进行编译时,我得到了这个有用的外观但令人困惑的错误消息:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in generic type due to conflicting requirements
  --> src/main.rs:48:31
   |
48 |     inner_mut = InnerMut::new(foo);
   |                               ^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 49:25...
  --> src/main.rs:49:25
   |
49 |       capturing_closure = move |mut borrow: &mut [u8]| {
   |  _________________________^
50 | |         inner_mut.mutate(&mut borrow);
51 | |     };
   | |_____^
note: ...so that expression is assignable (expected &mut &mut [u8], found &mut &mut [u8])
  --> src/main.rs:50:26
   |
50 |         inner_mut.mutate(&mut borrow);
   |                          ^^^^^^^^^^^
note: but, the lifetime must be valid for the block suffix following statement 2 at 46:5...
  --> src/main.rs:46:5
   |
46 | /     let mut inner_mut;
47 | |
48 | |     inner_mut = InnerMut::new(foo);
49 | |     capturing_closure = move |mut borrow: &mut [u8]| {
...  |
53 | |     store_fn_mut.run_closure_on_mutable_borrow();
54 | | }
   | |_^
note: ...so that variable is valid at time of its declaration
  --> src/main.rs:46:9
   |
46 |     let mut inner_mut;
   |         ^^^^^^^^^^^^^

2 个答案:

答案 0 :(得分:1)

我无法想到&mut &mut _的用例。

如果您将foo更改为

fn foo(borrow: &mut [u8], val: u32);

然后你又得到了另一个错误:

error[E0277]: the trait bound `[u8]: std::marker::Sized` is not satisfied
  --> src/main.rs:46:25
   |
46 |     let mut inner_mut = InnerMut::new(foo);
   |                         ^^^^^^^^^^^^^ `[u8]` does not have a constant size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `[u8]`
note: required by `<InnerMut<T>>::new`

嗯,此代码中T不需要Sized,因为它只在引用中使用,所以让我们添加约束T: ?Sized

pub struct InnerMut<T: ?Sized> {
    state: u32,
    stored_fn: fn(&mut T, u32),
}

impl<T: ?Sized> InnerMut<T> {
    // …
}

And this works.

答案 1 :(得分:1)

您遇到的是编译器无法证明您没有将&mut borrow内的mutate()引用存储到InnerMut实例中。这将是有问题的,因为它知道闭包的参数比闭包本身更短。但是InnerMut已移至关闭状态,且活动时间必须超过borrow

基本上Rust会阻止闭包参数转义闭包because it does not know how to infer lifetimes then

考虑这个最小的例子:

struct Test<T> {
    field: fn(T),
}

impl<T> Test<T> {
    fn foo(&self, _val: T) {}
}

fn calc(_: &mut i32) {}

fn main() {
    let test: Test<&mut i32> = Test { field: calc };

    let _ = move |y: i32| {
        test.foo(&mut y);
    };
}

它以某种方式编写,以便编译器更好地理解它,以便我们能够理解错误:

error[E0597]: `y` does not live long enough
  --> src/main.rs:15:23
   |
15 |         test.foo(&mut y);
   |                       ^ borrowed value does not live long enough
16 |     };
   |     - `y` dropped here while still borrowed
17 | }
   | - borrowed value needs to live until here

但我的结构中甚至没有那种类型的字段

Rust的一个关键原则是您的函数签名是错误报告的障碍。根据签名检查函数本身,并根据签名检查调用者。这可以防止将函数体的混淆错误报告给函数的调用者(甚至没有编写它们)。

对于Rust知道的所有内容,您的T被推断为&mut u[8]而您的mutate()会抓住一个可变的自我。这是可疑的。更好地防止关闭变量的潜在逃逸。

但稍微更改代码会使其正常工作

拒绝所有不正确的程序并接受所有正确的程序是不可判定的。因此,Rust会谨慎行事并拒绝正确的程序。因此,一些细微的变化可以使Rust接受该程序,即使该程序之前是正确的。

这对我的代码意味着什么?

我真的不太了解编译器来回答这个问题。我的猜测是,通过将T更改为[u8]并且InnerMut类型缺少显式生存期,编译器可以证明您的闭包变量没有转义。