参数类型可能不会存活足够长时间(再次?)(在闭包中)

huangapple go评论72阅读模式
英文:

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` 可能无法存活足够长时间
 --&gt; 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&#39;s apparently just the DomainType of the closure trait.  I&#39;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&lt;T&gt;` 表示 `T` 是 `&#39;static`。

然而,泛型不一定是这样的。泛型可以包含任何类型的寿命。为了匹配两者,你需要明确注释寿命。

## 解决方案 1

你**可以**为泛型添加 `&#39;static` 注释,如下所示:

```rust
fn make&lt;DomainType: &#39;static&gt;(foo: fn(DomainType)) -&gt; Box&lt;dyn Fn(DomainType)&gt; {
    Box::new(foo)
}

然而,如果你实际上遇到了泛型带有寿命的情况,这会导致问题:

fn my_func(x: &amp;str) {
    println!("my_func: {}", x);
}

fn main() {
    let x = "abc".to_string();

    let f = make(my_func);
    f(&amp;x);
}
error[E0597]: `x` does not live long enough
  --> src/main.rs:13:7
   |
13 |     f(&amp;x);
   |     --^^-
   |     | |
   |     | borrowed value does not live long enough
   |     argument requires that `x` is borrowed for `&#39;static`
14 | }
   | - `x` dropped here while still borrowed

解决方案 2

在这里的正确方式是通过告诉编译器泛型附加的寿命与包含在 Box 中的寿命匹配来解决初始不匹配的问题。

像这样:

fn make&lt;&#39;a, DomainType: &#39;a&gt;(foo: fn(DomainType)) -&gt; Box&lt;dyn Fn(DomainType) + &#39;a&gt; {
    Box::new(foo)
}

现在它可以工作了:

fn my_func(x: &amp;str) {
    println!("my_func: {}", x);
}

fn main() {
    let x = "abc".to_string();

    let f = make(my_func);
    f(&amp;x);
}
my_func: abc

问题

但是这仍然存在问题。我不确定这些问题如何解决。

问题在于生成的 Box 对象将始终与特定寿命绑定。例如,看看这个例子:

fn make&lt;&#39;a, DomainType: &#39;a&gt;(foo: fn(DomainType)) -&gt; Box&lt;dyn Fn(DomainType) + &#39;a&gt; {
    Box::new(foo)
}

fn my_func(x: &amp;str) {
    println!("my_func: {}", x);
}

fn make2() -&gt; Box&lt;dyn Fn(&amp;str)&gt; {
    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&lt;&#39;r&gt; Fn(&amp;&#39;r str)`
              found trait object `dyn Fn(&amp;str)`

f2 可以接受可以借用的所有对象。f 只能接受具有相同寿命的对象,因为 &#39;a 在调用 make 时解析,而不是在执行 f 时。

另一个例子是这样的:

fn make&lt;&#39;a, DomainType: &#39;a&gt;(foo: fn(DomainType)) -&gt; Box&lt;dyn Fn(DomainType) + &#39;a&gt; {
    Box::new(foo)
}

fn my_func(x: &amp;str) {
    println!("my_func: {}", x);
}

fn make2() -&gt; Box&lt;dyn Fn(&amp;str)&gt; {
    Box::new(|x| println!("closure: {}", x))
}

fn main() {
    let f = make(my_func);
    let f2 = make2();

    {
        let x = "abc".to_string();
        f2(&amp;x); // 可以工作
        f(&amp;x); // 无法编译通过
    }
}
error[E0597]: `x` does not live long enough
  --> src/main.rs:20:11
   |
20 |         f(&amp;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&lt;dyn Fn(&amp;str)&gt;`
   |
   = note: values in a scope are dropped in the opposite order they are defined

我认为这是不可解决的。我认为泛型不能携带通用寿命,它们只能携带特定的寿命。并且特定寿命在调用 make() 时解析,而不是在执行 f 时。

虽然一个详细的解释需要一个对 Rust 编译器有更深入了解的人。如果像这样的东西存在的话,我也会对它感兴趣(类似于 DomainType: for&lt;&#39;a&gt; 或类似的东西)。

我可以看到一个示例的输出中说需要 dyn for&lt;&#39;r&gt; Fn(&amp;&#39;r str)。但我不知道是否可以手动注释这样的东西,或者它是否是编译器的内部内容。但这将是所需的内容 - 不是 Box&lt;dyn Fn(DomainType) + &#39;a&gt;,而是需要类

英文:

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&lt;T&gt; means that T is &#39;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 &#39;static to the generic, like this:

fn make&lt;DomainType: &#39;static&gt;(foo: fn(DomainType)) -&gt; Box&lt;dyn Fn(DomainType)&gt; {
    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: &amp;str) {
    println!(&quot;my_func: {}&quot;, x);
}

fn main() {
    let x = &quot;abc&quot;.to_string();

    let f = make(my_func);
    f(&amp;x);
}
error[E0597]: `x` does not live long enough
  --&gt; src/main.rs:13:7
   |
13 |     f(&amp;x);
   |     --^^-
   |     | |
   |     | borrowed value does not live long enough
   |     argument requires that `x` is borrowed for `&#39;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&lt;&#39;a, DomainType: &#39;a&gt;(foo: fn(DomainType)) -&gt; Box&lt;dyn Fn(DomainType) + &#39;a&gt; {
    Box::new(foo)
}

And it now works:

fn my_func(x: &amp;str) {
    println!(&quot;my_func: {}&quot;, x);
}

fn main() {
    let x = &quot;abc&quot;.to_string();

    let f = make(my_func);
    f(&amp;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&lt;&#39;a, DomainType: &#39;a&gt;(foo: fn(DomainType)) -&gt; Box&lt;dyn Fn(DomainType) + &#39;a&gt; {
    Box::new(foo)
}

fn my_func(x: &amp;str) {
    println!(&quot;my_func: {}&quot;, x);
}

fn make2() -&gt; Box&lt;dyn Fn(&amp;str)&gt; {
    Box::new(|x| println!(&quot;closure: {}&quot;, x))
}

fn main() {
    let x = &quot;abc&quot;.to_string();

    let f = make(my_func);
    let f2 = make2();

    let fs = [f2, f];
}
error[E0308]: mismatched types
  --&gt; src/main.rs:19:19
   |
19 |     let fs = [f2, f];
   |                   ^ one type is more general than the other
   |
   = note: expected trait object `dyn for&lt;&#39;r&gt; Fn(&amp;&#39;r str)`
              found trait object `dyn Fn(&amp;str)`

f2 here takes all objects that can be borrowed. f only takes objects that all can be borrowed with the same lifetime, as &#39;a resolves when make is called, not when f is executed.

Another example is this:

fn make&lt;&#39;a, DomainType: &#39;a&gt;(foo: fn(DomainType)) -&gt; Box&lt;dyn Fn(DomainType) + &#39;a&gt; {
    Box::new(foo)
}

fn my_func(x: &amp;str) {
    println!(&quot;my_func: {}&quot;, x);
}

fn make2() -&gt; Box&lt;dyn Fn(&amp;str)&gt; {
    Box::new(|x| println!(&quot;closure: {}&quot;, x))
}

fn main() {
    let f = make(my_func);
    let f2 = make2();

    {
        let x = &quot;abc&quot;.to_string();
        f2(&amp;x); // Works
        f(&amp;x); // Fails to compile
    }
}
error[E0597]: `x` does not live long enough
  --&gt; src/main.rs:20:11
   |
20 |         f(&amp;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&lt;dyn Fn(&amp;str)&gt;`
   |
   = 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&lt;&#39;a&gt; or similar)

I can see that one of the example's output says it would require a dyn for&lt;&#39;r&gt; Fn(&amp;&#39;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&lt;dyn Fn(DomainType) + &#39;a&gt;, we would need something like Box&lt;dyn for &lt;&#39;r&gt; Fn(DomainType + &#39;r)&gt;. 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&lt;F, DomainType&gt;(foo: F) -&gt; Box&lt;dyn Fn(DomainType)&gt;
where
    for&lt;&#39;r&gt; F: Fn(DomainType) + Clone + &#39;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&lt;F, DomainType&gt;(foo: F) -&gt; Box&lt;dyn Fn(DomainType)&gt; 
where 
    for&lt;&#39;r&gt; F: Fn(DomainType) + &#39;r 
{
    Box::new(foo)
} 

Unless you want to pass in a reference to a closure that doesn't get consumed:

fn make&lt;F, DomainType&gt;(foo: &amp;F) -&gt; Box&lt;dyn Fn(DomainType)&gt; 
where 
    for&lt;&#39;r&gt; F: Fn(DomainType) + Clone + &#39;r 
{
    Box::new(foo.clone())
} 

huangapple
  • 本文由 发表于 2023年1月9日 08:15:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/75052215.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定