簡體   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