【问题标题】:How to express lifetime restrictions of a closure to match a trait bounded lifetime?如何表达闭包的生命周期限制以匹配特征有界生命周期?
【发布时间】:2020-07-26 08:20:20
【问题描述】:

我有一个特性,它返回一个附加到它自己生命周期的借用:

trait SomeTrait {
    fn do<'a>(&'a self, other: &AnonymousLifetime) -> &'a Output;
}

如何在where clause 中表达相同的限制,以便SomeTrait 可以impl From&lt;Closure&gt;

示例

场景的minimal, reproducible example (playground):

// The traits
trait Context {
    fn give(&self) -> usize;
}
trait ContextDecider {
    fn decide<'a>(&'a self, context: &dyn Context) -> &'a str;
}

// A concrete implementation example
// As expected, works OK
struct SomeDecider(Vec<String>);
impl ContextDecider for SomeDecider {
    fn decide<'a>(&'a self, context: &dyn Context) -> &'a str {
        let some_context = context.give();
        if some_context > self.0.len() {
            panic!("Oh no!");
        }

        &self.0[some_context]
    }
}

// An implemetation for a closure
// Help here!!
impl<'a, F> ContextDecider for F
where
    F: 'a + Fn(&dyn Context) -> &'a str,
{
    fn decide<'b>(&'b self, giver: &dyn Context) -> &'b str {
        self(giver)
    }
}

编译失败:

error[E0312]: lifetime of reference outlives lifetime of borrowed content...
  --> src/lib.rs:30:9
   |
30 |         self(giver)
   |         ^^^^^^^^^^^
   |
note: ...the reference is valid for the lifetime `'b` as defined on the method body at 29:15...
  --> src/lib.rs:29:15
   |
29 |     fn decide<'b>(&'b self, giver: &dyn Context) -> &'b str {
   |               ^^
note: ...but the borrowed content is only valid for the lifetime `'a` as defined on the impl at 25:6
  --> src/lib.rs:25:6
   |
25 | impl<'a, F> ContextDecider for F
   |      ^^

在示例中,我未能在闭包边界中表达特征所施加的限制并且编译器不满意。
编译器并没有帮助我使用什么语法来让我将两个生命周期锁定在一起。

【问题讨论】:

  • 看来How does for<> syntax differ from a regular lifetime bound? 的答案可能会回答您的问题。如果没有,请edit您的问题来解释差异。否则,我们可以将此问题标记为已回答。
  • @Shepmaster 链接不是不同的场景吗?例如,将生命周期与 HRTB 绑定我得到this。还是我错过了什么?我知道你真的很擅长将 Rust 问题保持在高水平,所以在我说它们不同之前,我在这里尽我所能应用提供的链接中的答案。但在某种程度上,问题是关于表达和语法,而答案并没有真正帮助。
  • 如果我 100% 确定建议的副本已回答了该问题,我会自己关闭它。这更像是试图在我有的几分钟内提供帮助。
  • 请注意,即使 this 代码有效,仍然无法使闭包返回对自身的引用:请参阅 Cannot infer an appropriate lifetime for a closure that returns a reference 以获得解释。恐怕您可能必须找到其他解决方案。

标签: rust closures traits lifetime


【解决方案1】:

你能在ContextDecider 上应用一个终身特征吗? (这样你就可以得到 ContextDeciderinstead of having the lifetime only ondecide`。)

这会导致:

trait Context {
    fn give(&self) -> usize;
}
trait ContextDecider<'a> {
    fn decide(&'a self, context: &dyn Context) -> &'a str;
}

struct SomeDecider(Vec<String>);
impl<'a> ContextDecider<'a> for SomeDecider {
    fn decide(&'a self, context: &dyn Context) -> &'a str {
        let some_context = context.give();
        if some_context > self.0.len() {
            panic!("Oh no!");
        }

        &self.0[some_context]
    }
}

impl<'f, F> ContextDecider<'f> for F
where
    F: 'f,
    for<'ctx>F: Fn(&'ctx dyn Context) -> &'f str,
{
    fn decide(&'f self, giver: &dyn Context) -> &'f str {
        self(giver)
    }
}

【讨论】:

  • 确实可以。但我是在能够在 where 子句的闭包边界中表达它之后。更改 trait 签名可能不可行或需要过多的重构。感谢您的回答,它将在许多其他情况下帮助许多开发人员并且有效地来到这里。不过,我不能将其标记为答案。
【解决方案2】:

您可以将函数包装在另一个结构中(这里称为FnWrapper):

trait Context {
    fn give(&self) -> usize;
}
impl Context for () {
    fn give(&self) -> usize {0}
}
trait ContextDecider {
    fn decide<'a>(&'a self, context: &dyn Context) -> &'a str;
}

struct SomeDecider(Vec<String>);
impl ContextDecider for SomeDecider {
    fn decide<'a>(&'a self, context: &dyn Context) -> &'a str {
        let some_context = context.give();
        if some_context > self.0.len() {
            panic!("Oh no!");
        }

        &self.0[some_context]
    }
}

struct FnWrapper<'a, F> {
    f: F,
    _phantom: std::marker::PhantomData<& 'a ()>,
}
impl<'a, F> FnWrapper<'a, F> 
    where F: 'a + Fn(&dyn Context) -> &'a str,
{
    fn new(f: F) -> Self {
        Self {
            f,
            _phantom: Default::default(),
        }
    }
    fn f_decide<'b>(&'b self, giver: &dyn Context) -> &'b str {
        (self.f)(giver)
    }
}

impl<'a, F> ContextDecider for FnWrapper<'a, F>
where
    F: 'a + Fn(&dyn Context) -> &'a str,
{
    fn decide<'b>(&'b self, giver: &dyn Context) -> &'b str {
        self.f_decide(giver)
    }
}

fn main() {
    println!("{}", FnWrapper::new(|giver| vec!["1", "2", "3"][giver.give()]).decide(&()));
}

【讨论】:

  • 我明白你在做什么。有趣的!您拥有闭包的所有权,从那里您可以参考selflifetime。这很棒,并请求尝试使用 From 进行包装,以查看闭包和特征之间的中间步骤是否可以透明
猜你喜欢
  • 2015-04-22
  • 1970-01-01
  • 2015-02-22
  • 1970-01-01
  • 2019-08-05
  • 2017-11-08
  • 1970-01-01
  • 1970-01-01
  • 2018-03-19
相关资源
最近更新 更多