Функция, на которую ссылаются из хранимой в структуре, не отказывается от владения - PullRequest
0 голосов
/ 10 января 2019

Я пишу тестовый стенд алгоритма для сравнения производительности в Rust.

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

struct Alg<'a, 'b, 'c> {
    alg1: &'c Fn(&'a A<'a>, &'b B<'b>) -> usize,
    alg2: &'c Fn(&'a A<'a>, &'b B<'b>) -> String,
}

struct A<'a> {
    a_str: &'a str,
}

struct B<'b> {
    b_str: &'b str,
}

fn concat<'a, 'b>(_a: &'a A<'a>, _b: &'b B<'b>) -> String {
    _a.a_str.to_string() + &_b.b_str.to_string()
}

fn length<'a, 'b>(_a: &'a A<'a>, _b: &'b B<'b>) -> usize {
    _a.a_str.len() + _b.b_str.len()
}

fn run1<'a, 'b, 'c>(_a: &'a A<'a>, _b: &'b B<'b>, _f_c: &'c Alg<'a, 'b, 'c>) {
    println!("{}", &(_f_c.alg1)(_a, _b));
}

fn run2<'a, 'b, 'c>(_a: &'a A<'a>, _b: &'b B<'b>, _f_c: &'c Alg<'a, 'b, 'c>) {
    println!("{}", &(_f_c.alg2)(_a, _b));
}

fn main() {
    let f_struct = Alg {
        alg1: &length,
        alg2: &concat,
    };

    for _i in 0..2 {
        let a_str = "ABC";
        let a = A { a_str: a_str };
        for _j in 0..2 {
            let b_str = "BCD";
            let b = B { b_str: b_str };
            println!("{}", concat(&a, &b)); // This works
            println!("{}", (f_struct.alg1)(&a, &b)); // I expect that `concat` or `length` in `f_struct` may finished borrowing `a` or `b' here, as like as `println!("{}",concat(&a,&b))`
                                                     //run1(&a,&b,&f_struct);
                                                     //run2(&a,&b,&f_struct);
        }
    }
}

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

error[E0597]: `a` does not live long enough
  --> src/main.rs:43:44
   |
43 |             println!("{}", (f_struct.alg1)(&a, &b)); // I expect that `concat` or `length` in `f_struct` may finished borrowing `a` or `b' here, as like as `println!("{}",concat(&a,&b))`
   |                            --------------- ^^ borrowed value does not live long enough
   |                            |
   |                            borrow used here, in later iteration of loop
...
47 |     }
   |     - `a` dropped here while still borrowed

error[E0597]: `b` does not live long enough
  --> src/main.rs:43:48
   |
43 |             println!("{}", (f_struct.alg1)(&a, &b)); // I expect that `concat` or `length` in `f_struct` may finished borrowing `a` or `b' here, as like as `println!("{}",concat(&a,&b))`
   |                            ---------------     ^^ borrowed value does not live long enough
   |                            |
   |                            borrow used here, in later iteration of loop
...
46 |         }
   |         - `b` dropped here while still borrowed

В чем разница между println!("{}",concat(&a,&b)) и println!("{}",(f_struct.alg1)(&a,&b))?

Я подумал, что должен указать что-то, что функция больше не заимствует значение с временем жизни 'a или 'b, но я не смог найти его в приложении ржавчины на примере или в книге ржавчины.

Я пытался применить принуждение, как 'c: 'a + 'b, но, похоже, это не помогло.

Эти вопросы связаны, но не очень понятны для меня.

Point

  • Я хочу хранить функции в структуре
    • Я мог бы попробовать другой способ, например, не хранить функции в структуре
    • Но я хочу понять причину, когда такой подход невозможен

1 Ответ

0 голосов
/ 10 января 2019

Быстрое решение

У вас слишком много указателей времени жизни. Удалите время жизни для ссылок в параметрах вашей функции. Например. заменить alg1: &'c Fn(&'a A<'a>, &'b B<'b>) -> usize на alg1: &'c Fn(&A<'a>, &B<'b>) -> usize (и аналогичные изменения для всех функций ( детская площадка ).

Объяснение

Во-первых, давайте немного упростим ваш код и переименуем некоторые из времен жизни, чтобы мы знали, о каком мы говорим:

struct Alg<'Alg_a, 'Alg_b> {
    alg1: &'Alg_b Fn(&'Alg_a A<'Alg_a>) -> usize,
}

struct A<'A_a> {
    a_str: &'A_a str,
}

fn length<'L_a>(a: &'L_a A<'L_a>) -> usize {
    a.a_str.len()
}

fn main() {
    let f_struct = Alg {
        alg1: &length,
    };

    for _i in 0..2 {
        let a_str = "ABC";
        let a = A { a_str: a_str };
        println!("{}", length (&a)); // This works
        println!("{}", (f_struct.alg1) (&a)); // This doesn't
    }
}

На игровой площадке 1015 * вы можете проверить, что в ней присутствует та же ошибка, что и в вашем коде.

Когда вы вызываете (f_struct.alg1)(&a), компилятор пытается найти правильные значения для времен жизни 'Alg_a и 'Alg_b, связанных с f_struct. Поскольку f_struct определено вне цикла, то эти времена жизни должны быть одинаковыми для всех итераций цикла. Однако Alg::alg1 определяется как Fn(&'Alg_a …), что означает, что 'Alg_a должно быть временем жизни параметра a, который действителен только для одной итерации цикла . Отсюда и ошибка.

Не указывая время жизни параметра, я позволяю компилятору выбирать разные времена жизни для параметра a и для 'Alg_a, и, в частности, выбирать другое время жизни для параметра при каждом вызове функции. Таким образом, время жизни параметра может быть ограничено одной итерацией цикла, в то время как 'Alg_a может быть длиннее:

struct Alg<'Alg_a, 'Alg_b> {
    alg1: &'Alg_b Fn(&A<'Alg_a>) -> usize,
}

struct A<'A_a> {
    a_str: &'A_a str,
}

fn length<'L_a>(a: &A<'L_a>) -> usize {
    a.a_str.len()
}

fn main() {
    let f_struct = Alg {
        alg1: &length,
    };

    for _i in 0..2 {
        let a_str = "ABC";
        let a = A { a_str: a_str };
        println!("{}", length (&a)); // This works
        println!("{}", (f_struct.alg1) (&a)); // Now this does too
    }
}

детская площадка

Почему звонит length напрямую?

При непосредственном вызове length компилятору нужно только определить время жизни 'L_a, и ничто не требует, чтобы это время жизни длилось более одной итерации цикла, поэтому конфликта нет.

Примечание

Как отмечает @VikramFugro, это работает только потому, что a_str = "ABC" создает срез со сроком действия 'static, который может быть уменьшен до 'Alg_a или 'L_a при необходимости. Использование динамической строки (let a_str = String::from("ABC")) не работает . Нам нужно объявить alg1 как &'Alg_b for<'F_a> Fn(&A<'F_a>) -> usize вместо использования времени жизни 'Alg_a в структуре Alg:

struct Alg<'Alg_b> {
    alg1: &'Alg_b for<'F_a> Fn(&A<'F_a>) -> usize,
}

детская площадка

Кроме того, Rust 2018 позволяет нам использовать анонимное время жизни '_ вместо синтаксиса for<'a> …, например &'Alg_b Fn(&A<'_>) -> usize ( детская площадка ).

...