繁体   English   中英

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

[英]Why do I get the error “cannot infer an appropriate lifetime for lifetime parameter in generic type” when using nested mutable references?

编码时习惯了Rust,我偶然发现编译错误。 我想了解为什么我得到错误以及如何处理它:

由于需求冲突,无法推断泛型类型的生命周期参数的适当生命周期

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

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

游乐场链接 (略有不同的错误信息)

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;
   |         ^^^^^^^^^^^^^

我不可能想到&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> {
    // …
}

这很有效。

您遇到的是编译器无法证明您没有InnerMut mutate()内部的&mut borrow的引用存储到您的InnerMut实例中。 这将是有问题的,因为它知道闭包的参数比闭包本身更短。 但是InnerMut被转移到关闭,并且必须比borrow更长寿。

基本上Rust会阻止闭包参数转义, 因为它不知道如何推断生命周期

考虑这个最小的例子:

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()捕获了一个可变的self。 这是可疑的。 更好地防止封闭变量的潜在逃逸。

但稍微更改代码使其工作

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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