Как вернуть закрытие Rust, которое заимствует состояние, чтобы использовать позже? - PullRequest
2 голосов
/ 02 октября 2019

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

parse_string - это функция, которая принимает ссылку на строку и возвращает замыкание для использования позже,вот код:

fn main() {
    let parse_this = parse_string(&String::from("Hello!"));
    println!("{}", parse_this("goodbye!"));
}

fn parse_string(string: &String) -> impl Fn(&str) -> &String {
    return |targetString| {
        // pretend there is parsing logic
        println!("{}", targetString);
        return string;
    };
}

Ошибка компилятора:

error: cannot infer an appropriate lifetime
  --> src/main.rs:7:12
   |
6  |   fn parse_string(string: &String) -> impl Fn(&str) -> &String {
   |                                       ------------------------ this return type evaluates to the `'static` lifetime...
7  |       return |targetString| {
   |  ____________^
8  | |         // pretend there is parsing logic
9  | |         println!("{}", targetString);
10 | |         return string;
11 | |     };
   | |_____^ ...but this borrow...
   |
note: ...can't outlive the anonymous lifetime #1 defined on the function body at 6:1
  --> src/main.rs:6:1
   |
6  | / fn parse_string(string: &String) -> impl Fn(&str) -> &String {
7  | |     return |targetString| {
8  | |         // pretend there is parsing logic
9  | |         println!("{}", targetString);
10 | |         return string;
11 | |     };
12 | | }
   | |_^
help: you can add a constraint to the return type to make it last less than `'static` and match the anonymous lifetime #1 defined on the function body at 6:1
   |
6  | fn parse_string(string: &String) -> impl Fn(&str) -> &String + '_ {
   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0312]: lifetime of reference outlives lifetime of borrowed content...
  --> src/main.rs:10:16
   |
10 |         return string;
   |                ^^^^^^
   |
note: ...the reference is valid for the anonymous lifetime #2 defined on the body at 7:12...
  --> src/main.rs:7:12
   |
7  |       return |targetString| {
   |  ____________^
8  | |         // pretend there is parsing logic
9  | |         println!("{}", targetString);
10 | |         return string;
11 | |     };
   | |_____^
note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the function body at 6:1
  --> src/main.rs:6:1
   |
6  | / fn parse_string(string: &String) -> impl Fn(&str) -> &String {
7  | |     return |targetString| {
8  | |         // pretend there is parsing logic
9  | |         println!("{}", targetString);
10 | |         return string;
11 | |     };
12 | | }
   | |_^

Ответы [ 2 ]

2 голосов
/ 02 октября 2019

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

Fn(&str) -> &String будет типом для функции, которая возвращает&String того же времени жизни, что и &str;то есть for<'b> Fn(&'b str) -> &'b String. Вы должны сказать, что возвращенный &String имеет то же время жизни, что и &String, переданный в parse_string:

fn parse_string<'a>(string: &'a String) -> impl Fn(&str) -> &'a String {

Обратите внимание, что Fn(&str) не имеет аннотации времени жизни;это потому, что время жизни &str, переданное в замыкание , не связано с временем жизни &String, переданным в parse_string.

Для того, чтобы parse_string компилироваласьВам нужно сделать еще одно изменение. Замыкания пытаются заимствовать свое окружение, если компилятор считает, что его не нужно перемещать. Ваше замыкание, которое занимает string, не может быть возвращено функцией, где string является локальной переменной. Чтобы исправить это, вы move захватили переменную в замыкании:

    move |target_string| {
        // pretend there is parsing logic
        println!("{}", target_string);
        string
    }

В Rust идиоматично опускать return в последнем выражении в функции.

Также обратите внимание&String - необычный тип, потому что он не предлагает выразительности, которую &str не обеспечивает. Почти всегда ошибка иметь &String в неуниверсальном коде. См. Почему не рекомендуется принимать ссылку на String (& String), Vec (& Vec) или Box (& Box) в качестве аргумента функции? для получения дополнительной информации.

Помещение всего этоговместе, вот как я бы написал parse_string:

fn parse_string<'a>(string: &'a str) -> impl Fn(&str) -> &'a str {
    move |target_string| {
        // pretend there is parsing logic
        println!("{}", target_string);
        string
    }
}

Ваш main также нуждается в небольшой настройке: &String::from("Hello!") принимает ссылку на временный String, который будет немедленно отброшен вконец строки, делает недействительной ссылку. Это легко исправить, сохранив String в переменной, поэтому он не будет удален до конца области действия:

fn main() {
    let hello = String::from("Hello!");
    let parse_this = parse_string(&hello);
    println!("{}", parse_this("goodbye!"));
}
2 голосов
/ 02 октября 2019

У вас есть несколько проблем составления:

  1. Вам необходимо явное время жизни, чтобы связать время жизни аргумента string со временем жизни возвращаемого значения возвращаемого замыкания. Прямо сейчас, life elision заставляет его выводить то же самое, что и аргумент для замыкания.

  2. Невозможно вернуть ссылку навременно через функцию. Это должна быть отдельная переменная.

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

Дополнительно ...

  1. targetString должно быть target_string, чтобы следовать идиомам Rust.
  2. return не следует использовать в концеблока, следующего за идиомами Rust.
  3. &str обычно предпочтительнее &String
fn main() {
    let s = String::from("Hello!");
    let parse_this = parse_string(&s);
    println!("{}", parse_this("goodbye!"));
}

fn parse_string<'a>(string: &'a String) -> impl Fn(&str) -> &'a String {
    return move |target_string| {
        // pretend there is parsing logic
        println!("{}", target_string);
        string
    };
}

См. также:

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