从存储在结构中引用的函数不会放弃所有权
Function refered from the stored in struct does not give the ownership up
我正在编写一个算法测试平台来比较 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
那样应用强制,但这似乎无济于事。
这些问题是相关的,但我不是很清楚。
- Rust lifetime error expected concrete lifetime but found bound lifetime
点数
- 我想在结构中存储函数
- 我可以尝试另一种方式,比如不在结构中存储函数
- 但我想了解这种方法不可能的原因
快速解决
您的生命周期说明符太多。删除函数参数中引用的生命周期。例如。将 alg1: &'c Fn(&'a A<'a>, &'b B<'b>) -> usize
替换为 alg1: &'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)
时,编译器会尝试为与 f_struct
关联的生命周期 'Alg_a
和 'Alg_b
找到合适的值。由于 f_struct
是在循环外定义的,因此对于循环的 all 次迭代,这些生命周期必须相同。但是 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")
) does not work。我们需要将 alg1
声明为 &'Alg_b for<'F_a> Fn(&A<'F_a>) -> usize
而不是在 Alg
结构上使用 'Alg_a
生命周期:
struct Alg<'Alg_b> {
alg1: &'Alg_b for<'F_a> Fn(&A<'F_a>) -> usize,
}
此外,Rust 2018 允许我们使用匿名生命周期 '_
而不是 for<'a> …
语法,例如 &'Alg_b Fn(&A<'_>) -> usize
(playground).
我正在编写一个算法测试平台来比较 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
那样应用强制,但这似乎无济于事。
这些问题是相关的,但我不是很清楚。
- Rust lifetime error expected concrete lifetime but found bound lifetime
点数
- 我想在结构中存储函数
- 我可以尝试另一种方式,比如不在结构中存储函数
- 但我想了解这种方法不可能的原因
快速解决
您的生命周期说明符太多。删除函数参数中引用的生命周期。例如。将 alg1: &'c Fn(&'a A<'a>, &'b B<'b>) -> usize
替换为 alg1: &'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)
时,编译器会尝试为与 f_struct
关联的生命周期 'Alg_a
和 'Alg_b
找到合适的值。由于 f_struct
是在循环外定义的,因此对于循环的 all 次迭代,这些生命周期必须相同。但是 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")
) does not work。我们需要将 alg1
声明为 &'Alg_b for<'F_a> Fn(&A<'F_a>) -> usize
而不是在 Alg
结构上使用 'Alg_a
生命周期:
struct Alg<'Alg_b> {
alg1: &'Alg_b for<'F_a> Fn(&A<'F_a>) -> usize,
}
此外,Rust 2018 允许我们使用匿名生命周期 '_
而不是 for<'a> …
语法,例如 &'Alg_b Fn(&A<'_>) -> usize
(playground).