簡體   English   中英

如何在 Rust Closure 中返回引用

[英]How to return a reference in Rust Closure

我有以下 rust 代碼無法編譯。

struct Person {
    name : String,
    age : u8,
}


fn main() {
    let p = Person{ name: "Nobody".to_string(), age : 24};

    let age = |p : &Person| p.age;
    let name = |p : &Person | &p.name;

    println! ("name={}, age={}" , name(&p), age(&p));
}

並且編譯器給出了以下錯誤信息。

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
  --> src/main.rs:11:31
   |
11 |     let name = |p : &Person | &p.name;
   |                               ^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 11:16...
  --> src/main.rs:11:16
   |
11 |     let name = |p : &Person | &p.name;
   |                ^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:11:31
   |
11 |     let name = |p : &Person | &p.name;
   |                               ^^^^^^^
note: but, the lifetime must be valid for the expression at 2:29...
  --> src/main.rs:13:5
   |
13 |     println! ("name={}, age={}" , name(&p), age(&p));
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so type `(&&std::string::String, &u8)` of expression is valid during the expression
  --> src/main.rs:13:5
   |
13 |     println! ("name={}, age={}" , name(&p), age(&p));
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

error: aborting due to previous error

我試圖為name關閉添加生命周期。

let name<'a> = |p : &'a Person | -> &'a String { &'a p.name };

但仍然有編譯器錯誤

   Compiling playground v0.0.1 (/playground)
error: expected one of `:`, `;`, `=`, `@`, or `|`, found `<`
  --> src/main.rs:12:13
   |
12 |     let name<'a> = |p : &'a Person | -> &'a String { &'a p.name };
   |             ^ expected one of `:`, `;`, `=`, `@`, or `|`

error: aborting due to previous error

只是想知道如何編寫正確的代碼。

對我來說更重要的是了解問題的根源找到解決方法,所以一步一步來。 讓我們從有效的東西開始:

struct Person {
    name: String,
    age: u8,
}

fn get_name<'a>(person: &'a Person) -> &'a str {
    &person.name
}

fn main() {
    let p = Person {
        name: "Nobody".to_string(),
        age: 24,
    };

    let age = |p: &Person| p.age;
    let name = get_name;

    println!("name={}, age={}", name(&p), age(&p));
}

使用函數而不是閉包時沒有問題。 在這種情況下,編譯器能夠檢查生命周期要求是否正常。

但是當嘗試對name使用閉包時:

let name = |p : &Person | &p.name;

你得到了cannot infer an appropriate lifetime錯誤。

為什么?

閉包捕獲其環境:編譯器必須創建一些不透明的結構,並且此類結構必須是可調用的。

我不完全了解內部細節,但是在對閉包進行脫糖時會創建以下內容:

struct OpaqueType<'a> {
    // a PhantomData because you don't capure nothing
    // just to make explicit that struct lifetime bind to environment
    // if you would had captured some integer:
    // captured_int: &'a i32,
    captured_int: PhantomData<&'a i32>,

}

impl<'a> OpaqueType<'a> {
    fn call<'b>(&'b self, person: &'a Person) -> &'a str {
        &person.name
    }
}

看看call很明顯,當一個 clusure 參數是一個引用時,有兩個不相關的生命周期在起作用。

最終答案:如何返回引用

另請注意,在您的情況下,不聲明參數類型並使用輔助函數get_name ,有效:

// let name = |p| &p.name; // does not work, not enough info to infer p type 
let name = |p| get_name(p);

我的猜測是,在這種情況下,編譯器遵循一些推理路徑,能夠以生命周期按預期有界的方式進行脫糖。

另一種解決方案是為您的閉包提供顯式類型。 不幸的是,您不能使用它的實際類型,但您可以將其轉換為函數指針。

請記住,問題在於編譯器無法正確推斷輸出的生命周期與輸入的生命周期相關(它可能是此錯誤的一個實例,但我完全不確定)。 我們可以通過使生命周期顯式來解決這個問題。

struct Person {
    name: String,
    age: u8,
}

fn main() {
    let p = Person {
        name: "Nobody".to_string(),
        age: 24,
    };

    let age = |p: &Person| p.age;
    // Our only changes are right here.
    let name: for<'a> fn(&'a Person) -> &'a String = |p: &Person| &p.name;

    println!("name={}, age={}", name(&p), age(&p));
}

(操場)

事實上,有可能比這稍微不那么明確。 編譯器可以很好地確定輸入和輸出的類型。 這只是它遇到麻煩的生命周期。 所以用let name: for<'a> fn(&'a _) -> &'a _ = |p: &Person| &p.name;替換該行let name: for<'a> fn(&'a _) -> &'a _ = |p: &Person| &p.name; let name: for<'a> fn(&'a _) -> &'a _ = |p: &Person| &p.name; 也適用(游樂場)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM