«не может определить подходящее время жизни» при использовании замыкания для возврата ссылки на содержимое варианта enum - PullRequest
0 голосов
/ 13 февраля 2019

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

Я хотел бы написать функцию, которая действует как фильтр и возвращает Option::Some, содержащий ссылку на содержимое варианта enum, или None, если значение должно быть отброшено.

Далее следуетминимальный рабочий (не совсем компилируемый) пример:

enum Data<'a> {
    Value(&'a String),
    Null,
}

fn main() {
    let s = String::new();
    let d = Data::Value(&s);

    let equal = |d: &Data| -> Option<&String> {
        if let Data::Value(s) = d {
            Some(s)
        } else {
            None
        }
    };

    parse(&d, equal);
    //parse(&d, equal_filter);
}

fn equal_filter<'a>(d: &'a Data) -> Option<&'a String> {
    if let Data::Value(s) = d {
        Some(s)
    } else {
        None
    }
}

fn parse<'a, F>(data: &Data<'a>, filter: F)
where
    F: Fn(&Data<'a>) -> Option<&'a String>,
{
    filter(data);
}

Playground .

Сначала я попытался скомпилировать код с помощью замыкания, но в этом случае я получаю ошибку:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:11:33
   |
11 |         if let Data::Value(s) = d {
   |                                 ^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 10:17...
  --> src/main.rs:10:17
   |
10 |       let equal = |d: &Data| -> Option<&String> {
   |  _________________^
11 | |         if let Data::Value(s) = d {
12 | |             Some(s)
13 | |         } else {
14 | |             None
15 | |         }
16 | |     };
   | |_____^
   = note: ...so that the types are compatible:
           expected &Data<'_>
              found &Data<'_>
note: but, the lifetime must be valid for the expression at 18:5...
  --> src/main.rs:18:5
   |
18 |     parse(&d, equal);
   |     ^^^^^
note: ...so that a type/lifetime parameter is in scope here
  --> src/main.rs:18:5
   |
18 |     parse(&d, equal);
   |     ^^^^^

Итак, я попытался с функцией, но получил другую ошибку:

error[E0271]: type mismatch resolving `for<'r> <for<'a, 's> fn(&'a Data<'s>) -> std::option::Option<&'a std::string::String> {equal_filter} as std::ops::FnOnce<(&'r Data<'_>,)>>::Output == std::option::Option<&std::string::String>`
  --> src/main.rs:19:5
   |
19 |     parse(&d, equal_filter);
   |     ^^^^^ expected bound lifetime parameter, found concrete lifetime
   |
note: required by `parse`
  --> src/main.rs:30:1
   |
30 | / fn parse<'a, F>(data: &Data<'a>, filter: F)
31 | | where
32 | |     F: Fn(&Data<'a>) -> Option<&'a String>,
33 | | {
34 | |     filter(data);
35 | | }
   | |_^

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

1 Ответ

0 голосов
/ 13 февраля 2019

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

Вставьте ваше закрытие и он работает:

enum Data<'a> {
    Value(&'a String),
    Null,
}

fn main() {
    let s = String::new();
    let d = Data::Value(&s);

    parse(&d, |d| match d {
        Data::Value(s) => Some(s),
        _ => None,
    });
}

fn parse<'a, F>(data: &Data<'a>, filter: F)
where
    F: Fn(&Data<'a>) -> Option<&'a String>,
{
    filter(data);
}

Однако я 'Мы рекомендуем вам вместо этого создавать методы для перечисления и участвовать в идиоматическом наборе функций преобразования :

enum Data<'a> {
    Value(&'a String),
    Null,
}

impl<'a> Data<'a> {
    fn as_value(&self) -> Option<&'a str> {
        match self {
            Data::Value(s) => Some(s),
            _ => None,
        }
    }
}

fn main() {
    let s = String::new();
    let d = Data::Value(&s);

    parse(&d, Data::as_value);
}

fn parse<'a, F>(data: &Data<'a>, filter: F)
where
    F: Fn(&Data<'a>) -> Option<&'a str>,
{
    filter(data);
}

Ваш вариант функции не работает, потому что вы установили соответствующее время жизнив неправильном месте:

// Wrong
fn equal_filter<'a>(d: &'a Data) -> Option<&'a String>
// Right
fn equal_filter<'a>(d: &Data<'a>) -> Option<&'a String>

Использование #[deny(elided_lifetimes_in_paths)] или #[deny(rust_2018_idioms)] поможет вам в этом:

error: hidden lifetime parameters in types are deprecated
  --> src/main.rs:12:22
   |
12 |     let equal = |d: &Data| -> Option<&String> {
   |                      ^^^^- help: indicate the anonymous lifetime: `<'_>`
   |
error: hidden lifetime parameters in types are deprecated
  --> src/main.rs:24:28
   |
24 | fn equal_filter<'a>(d: &'a Data) -> Option<&'a String> {
   |                            ^^^^- help: indicate the anonymous lifetime: `<'_>`

См. также:

...