英文:
the parameter type may not live long enough (again?) (in closure)
问题
在尝试编译以下代码时:
```rust
fn make<DomainType>(foo: fn(DomainType) -> ()) -> Box<dyn Fn(DomainType) -> ()> {
let foo_clone = foo.clone();
let bar = move |input: DomainType| {foo_clone(input)};
Box::new(bar)
}
fn main() {}
编译器报错如下:
error[E0310]: 参数类型 `DomainType` 可能无法存活足够长时间
--> src/main.rs:4:5
|
4 | Box::new(bar)
| ^^^^^^^^^^^^^ ...以满足类型 `[closure@src/main.rs:3:15: 3:58]` 的必需生命周期边界
|
帮助:考虑添加一个显式的生命周期边界...
|
1 | fn make<DomainType: 'static>(foo: fn(DomainType) -> ()) -> Box<dyn Fn(DomainType) -> ()> {
| +++++++++
要了解更多关于此错误的信息,请尝试运行 `rustc --explain E0310`。
我一直在阅读类似问题的解答:
https://stackoverflow.com/questions/29740488/parameter-type-may-not-live-long-enough
和
https://stackoverflow.com/questions/40053550/the-compiler-suggests-i-add-a-static-lifetime-because-the-parameter-type-may-no
但在这两个示例中,需要添加生命周期的是包装的 trait。而在这种情况下,似乎只是闭包 trait 的 DomainType
需要生命周期。我对此感到非常困惑。
我对函数定义中生命周期变量的理解是,它们将返回类型的生命周期(在此处可以理解为隐含为静态,或者至少盒子中的内容是静态的)与函数参数的生命周期(可能是省略的)连接起来。但在这里没有任何函数参数是 DomainType
。
<details>
<summary>英文:</summary>
In trying to compile the code
fn make<DomainType>(foo: fn(DomainType) -> ()) -> Box<dyn Fn(DomainType) -> ()> {
let foo_clone = foo.clone();
let bar = move |input: DomainType| {foo_clone(input)};
Box::new(bar)
}
fn main() {}
The compiler gives
error[E0310]: the parameter type DomainType
may not live long enough
--> src/main.rs:4:5
|
4 | Box::new(bar)
| ^^^^^^^^^^^^^ ...so that the type [closure@src/main.rs:3:15: 3:58]
will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound...
|
1 | fn make<DomainType: 'static>(foo: fn(DomainType) -> ()) -> Box<dyn Fn(DomainType) -> ()> {
| +++++++++
For more information about this error, try rustc --explain E0310
.
I have been reading through similar questions
https://stackoverflow.com/questions/29740488/parameter-type-may-not-live-long-enough
and
https://stackoverflow.com/questions/40053550/the-compiler-suggests-i-add-a-static-lifetime-because-the-parameter-type-may-no
But in both of those examples, the boxed trait is what needs the added lifetime. In this case it's apparently just the DomainType of the closure trait. I'm quite baffled what to make of this.
My understanding of lifetime variables in function definitions is that they connect the lifetime of the returned type (which I can understand is implicitly static here, or at least the thing inside the box is) to those (perhaps elided) lifetimes of the function parameters. But no function parameter here is a `DomainType`.
</details>
# 答案1
**得分**: 3
免责声明:这个问题涉及到 Rust 编译器的具体细节,我对我在这里说的一切都不是百分之百确定的。请谨慎对待所有内容。
问题在于,如果没有注释寿命(lifetime),`Box<T>` 表示 `T` 是 `'static`。
然而,泛型不一定是这样的。泛型可以包含任何类型的寿命。为了匹配两者,你需要明确注释寿命。
## 解决方案 1
你**可以**为泛型添加 `'static` 注释,如下所示:
```rust
fn make<DomainType: 'static>(foo: fn(DomainType)) -> Box<dyn Fn(DomainType)> {
Box::new(foo)
}
然而,如果你实际上遇到了泛型带有寿命的情况,这会导致问题:
fn my_func(x: &str) {
println!("my_func: {}", x);
}
fn main() {
let x = "abc".to_string();
let f = make(my_func);
f(&x);
}
error[E0597]: `x` does not live long enough
--> src/main.rs:13:7
|
13 | f(&x);
| --^^-
| | |
| | borrowed value does not live long enough
| argument requires that `x` is borrowed for `'static`
14 | }
| - `x` dropped here while still borrowed
解决方案 2
在这里的正确方式是通过告诉编译器泛型附加的寿命与包含在 Box 中的寿命匹配来解决初始不匹配的问题。
像这样:
fn make<'a, DomainType: 'a>(foo: fn(DomainType)) -> Box<dyn Fn(DomainType) + 'a> {
Box::new(foo)
}
现在它可以工作了:
fn my_func(x: &str) {
println!("my_func: {}", x);
}
fn main() {
let x = "abc".to_string();
let f = make(my_func);
f(&x);
}
my_func: abc
问题
但是这仍然存在问题。我不确定这些问题如何解决。
问题在于生成的 Box
对象将始终与特定寿命绑定。例如,看看这个例子:
fn make<'a, DomainType: 'a>(foo: fn(DomainType)) -> Box<dyn Fn(DomainType) + 'a> {
Box::new(foo)
}
fn my_func(x: &str) {
println!("my_func: {}", x);
}
fn make2() -> Box<dyn Fn(&str)> {
Box::new(|x| println!("closure: {}", x))
}
fn main() {
let x = "abc".to_string();
let f = make(my_func);
let f2 = make2();
let fs = [f2, f];
}
error[E0308]: mismatched types
--> src/main.rs:19:19
|
19 | let fs = [f2, f];
| ^ one type is more general than the other
|
= note: expected trait object `dyn for<'r> Fn(&'r str)`
found trait object `dyn Fn(&str)`
f2
可以接受可以借用的所有对象。f
只能接受具有相同寿命的对象,因为 'a
在调用 make
时解析,而不是在执行 f
时。
另一个例子是这样的:
fn make<'a, DomainType: 'a>(foo: fn(DomainType)) -> Box<dyn Fn(DomainType) + 'a> {
Box::new(foo)
}
fn my_func(x: &str) {
println!("my_func: {}", x);
}
fn make2() -> Box<dyn Fn(&str)> {
Box::new(|x| println!("closure: {}", x))
}
fn main() {
let f = make(my_func);
let f2 = make2();
{
let x = "abc".to_string();
f2(&x); // 可以工作
f(&x); // 无法编译通过
}
}
error[E0597]: `x` does not live long enough
--> src/main.rs:20:11
|
20 | f(&x); // 无法编译通过
| ^^ borrowed value does not live long enough
21 | }
| - `x` dropped here while still borrowed
22 | }
| - borrow might be used here, when `f` is dropped and runs the destructor for type `Box<dyn Fn(&str)>`
|
= note: values in a scope are dropped in the opposite order they are defined
我认为这是不可解决的。我认为泛型不能携带通用寿命,它们只能携带特定的寿命。并且特定寿命在调用 make()
时解析,而不是在执行 f
时。
虽然一个详细的解释需要一个对 Rust 编译器有更深入了解的人。如果像这样的东西存在的话,我也会对它感兴趣(类似于 DomainType: for<'a>
或类似的东西)。
我可以看到一个示例的输出中说需要 dyn for<'r> Fn(&'r str)
。但我不知道是否可以手动注释这样的东西,或者它是否是编译器的内部内容。但这将是所需的内容 - 不是 Box<dyn Fn(DomainType) + 'a>
,而是需要类
英文:
DISCLAIMER: This question goes very deep into Rust compiler specifics, and I'm not 100% sure about everything I say here. Take everything with a grain of salt.
The problem here is that Box<T>
means that T
is 'static
if no lifetime is annotated.
A generic, however, does not mean that. A generic could contain any kind of lifetime. To match the two, you need an explicit lifetime annotation.
Solution 1
You could annotate 'static
to the generic, like this:
fn make<DomainType: 'static>(foo: fn(DomainType)) -> Box<dyn Fn(DomainType)> {
Box::new(foo)
}
That, however, would create problems if you do actually encounter the case where the generic carries a lifetime:
fn my_func(x: &str) {
println!("my_func: {}", x);
}
fn main() {
let x = "abc".to_string();
let f = make(my_func);
f(&x);
}
error[E0597]: `x` does not live long enough
--> src/main.rs:13:7
|
13 | f(&x);
| --^^-
| | |
| | borrowed value does not live long enough
| argument requires that `x` is borrowed for `'static`
14 | }
| - `x` dropped here while still borrowed
Solution 2
The proper way here is to solve the initial mismatch by telling the compiler that the lifetimes attached to the generic match the lifetimes contained in the Box.
Like this:
fn make<'a, DomainType: 'a>(foo: fn(DomainType)) -> Box<dyn Fn(DomainType) + 'a> {
Box::new(foo)
}
And it now works:
fn my_func(x: &str) {
println!("my_func: {}", x);
}
fn main() {
let x = "abc".to_string();
let f = make(my_func);
f(&x);
}
my_func: abc
Problems
There are still problems with this. I'm unsure how/if they are solvable, though.
The problem is that the resulting Box
object will always be bound to a specific lifetime. Look for example at this:
fn make<'a, DomainType: 'a>(foo: fn(DomainType)) -> Box<dyn Fn(DomainType) + 'a> {
Box::new(foo)
}
fn my_func(x: &str) {
println!("my_func: {}", x);
}
fn make2() -> Box<dyn Fn(&str)> {
Box::new(|x| println!("closure: {}", x))
}
fn main() {
let x = "abc".to_string();
let f = make(my_func);
let f2 = make2();
let fs = [f2, f];
}
error[E0308]: mismatched types
--> src/main.rs:19:19
|
19 | let fs = [f2, f];
| ^ one type is more general than the other
|
= note: expected trait object `dyn for<'r> Fn(&'r str)`
found trait object `dyn Fn(&str)`
f2
here takes all objects that can be borrowed. f
only takes objects that all can be borrowed with the same lifetime, as 'a
resolves when make
is called, not when f
is executed.
Another example is this:
fn make<'a, DomainType: 'a>(foo: fn(DomainType)) -> Box<dyn Fn(DomainType) + 'a> {
Box::new(foo)
}
fn my_func(x: &str) {
println!("my_func: {}", x);
}
fn make2() -> Box<dyn Fn(&str)> {
Box::new(|x| println!("closure: {}", x))
}
fn main() {
let f = make(my_func);
let f2 = make2();
{
let x = "abc".to_string();
f2(&x); // Works
f(&x); // Fails to compile
}
}
error[E0597]: `x` does not live long enough
--> src/main.rs:20:11
|
20 | f(&x); // Fails to compile
| ^^ borrowed value does not live long enough
21 | }
| - `x` dropped here while still borrowed
22 | }
| - borrow might be used here, when `f` is dropped and runs the destructor for type `Box<dyn Fn(&str)>`
|
= note: values in a scope are dropped in the opposite order they are defined
I don't think this is solvable, though. I think generics can't carry generic lifetimes, they can only carry specific lifetimes. And said specific lifetime gets resolved at the point where make()
is called.
Although a proper explanation would require someone with a deeper Rust compiler understanding than me. If something like this exists, I'd be interested in it as well. (something like DomainType: for<'a>
or similar)
I can see that one of the example's output says it would require a dyn for<'r> Fn(&'r str)
. I don't know, however, if something like this is annotatable by hand or if it's something internal to the compiler. But that would be what is required - instead of Box<dyn Fn(DomainType) + 'a>
, we would need something like Box<dyn for <'r> Fn(DomainType + 'r)>
. Again, I don't know if/how this is annotatable, and if it is, I'd also be interested in it.
答案2
得分: 0
"Could Higher Rank Trait Bounds be what you're looking for?"
如果您正在寻找的是“高阶特质边界”,那么这可能是您需要的吗?
fn make<F, DomainType>(foo: F) -> Box<dyn Fn(DomainType)>
where
for<'r> F: Fn(DomainType) + Clone + 'r
{
let foo_clone = foo.clone();
let bar = move |input: DomainType| {foo_clone(input)};
Box::new(bar)
}
如果函数的目的是消耗一个闭包并将其返回为Box,我认为您不需要Clone特质。事实上,它甚至不需要嵌套在一个新的闭包中。只需直接放入Box中。
fn make<F, DomainType>(foo: F) -> Box<dyn Fn(DomainType)>
where
for<'r> F: Fn(DomainType) + 'r
{
Box::new(foo)
}
除非您想传递一个不会被消耗的闭包的引用:
fn make<F, DomainType>(foo: &F) -> Box<dyn Fn(DomainType)>
where
for<'r> F: Fn(DomainType) + Clone + 'r
{
Box::new(foo.clone())
}
英文:
Could Higher Rank Trait Bounds be what you're looking for?
fn make<F, DomainType>(foo: F) -> Box<dyn Fn(DomainType)>
where
for<'r> F: Fn(DomainType) + Clone + 'r
{
let foo_clone = foo.clone();
let bar = move |input: DomainType| {foo_clone(input)};
Box::new(bar)
}
If the purpose of the function is to consume a closure and return it in a Box, I don't think you need the Clone trait. In fact, it doesn't even need to be nested in a new closure. Just directly box it.
fn make<F, DomainType>(foo: F) -> Box<dyn Fn(DomainType)>
where
for<'r> F: Fn(DomainType) + 'r
{
Box::new(foo)
}
Unless you want to pass in a reference to a closure that doesn't get consumed:
fn make<F, DomainType>(foo: &F) -> Box<dyn Fn(DomainType)>
where
for<'r> F: Fn(DomainType) + Clone + 'r
{
Box::new(foo.clone())
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论