Как вернуть ссылку в Rust Closure - PullRequest
3 голосов
/ 28 марта 2020

У меня следующий код ржавчины не может быть скомпилирован.

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

Просто хочу узнать, как написать правильный код.

Ответы [ 2 ]

2 голосов
/ 28 марта 2020

Еще одно решение - дать явный тип вашему закрытию. К сожалению, вы не можете использовать его фактический тип, но вы можете привести его к указателю на функцию.

Помните, что проблема в том, что компилятор не может правильно сделать вывод, что время жизни вывода связано к времени жизни ввода (это может быть экземпляр этой ошибки , но я совсем не уверен). Мы можем исправить это, сделав время жизни явным.

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; также работает (детская площадка) .

2 голосов
/ 28 марта 2020

Для меня важнее понять источник проблемы, найти обходной путь, поэтому шаг за шагом. Давайте начнем с того, что работает:

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));
}

Нет проблем при использовании функции вместо clousure. В этом случае компилятор может проверить, что требования времени жизни в порядке.

Но при попытке использовать замыкание для 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, становится очевидным, что, когда аргумент кластера является ссылкой, существует два не связанных времени жизни при игре.

Наконец, ответ: как вернуть ссылку

Также обратите внимание, что в вашем случае, не объявляя тип аргумента и используя вспомогательную функцию get_name, работает:

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

Я предполагаю, что в таком случае компилятор, следуя некоторому пути логического вывода, способен десугаровать таким образом, что время жизни ограничено, как и ожидалось.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...