从存储在结构中引用的函数不会放弃所有权

标签 function rust lifetime

我正在编写一个算法测试平台来比较 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 的值,但我无法从 rust 中找到它-by-example 或 rust-book.

我试过像 'c: 'a + 'b 那样应用强制转换,但这似乎没有帮助。

这些问题是相关的,但对我来说不是很清楚。

  • 我想在结构中存储函数
    • 我可以尝试另一种方式,比如不在结构中存储函数
    • 但我想了解这种方法不可能的原因

最佳答案

快速解决方案

您的生命周期说明符太多。删除函数参数中引用的生命周期。例如。替换 alg1: &'c Fn(&'a A<'a>, &'b B<'b>) -> usizealg1: &'c Fn(&A<'a>, &B<'b>) -> usize (以及对所有函数的类似更改(playground)。

说明

首先,让我们稍微简化一下您的代码并重命名一些生命周期,以便我们知道我们在谈论的是哪一个:

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

您可以查看 playground这表现出与您的代码相同的错误。

当您调用 (f_struct.alg1)(&a) 时,编译器会尝试为生命周期找到好的值 'Alg_a'Alg_bf_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
    }
}

playground

为什么调用length直接上类?

调用length时直接,编译器只需要确定生命周期'L_a并且没有什么需要这个生命周期持续超过一个循环迭代,所以没有冲突。

注意事项

正如@VikramFugro 所指出的,这只有效,因为 a_str = "ABC"使用 'static 创建切片可以缩小到 'Alg_a 的生命周期或 'L_a按要求。使用动态字符串 ( let a_str = String::from("ABC") ) does not work .我们需要申报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,
}

playground

此外,Rust 2018 允许我们使用匿名生命周期 '_而不是 for<'a> …语法,例如 &'Alg_b Fn(&A<'_>) -> usize (playground)。

关于从存储在结构中引用的函数不会放弃所有权,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54124341/

相关文章:

javascript - 将作为参数传递的函数与数字关联起来

返回最大邻居索引的函数

static - 我可以避免在没有 `static mut` 的情况下重新编译我的平台层吗?

rust - 如何使用 Assembly (NASM) 引导加载程序编译 Rust 内核

javascript - 在 JavaScript 中,这种对变量进行赋值的函数有用吗?

function - 在 powershell 脚本中运行 Net Share 命令

rust - 将字符串转换为整数?

lambda - 为什么这个闭包需要内联或 `dyn` ? `dyn` 在这里做什么?

rust - 为什么变量的生命周期不够长?

reference - body 上定义的生命周期 'a 不一定比 body 上定义的匿名生命周期 #1 长