【问题标题】:Implementing a trait method returning a bounded lifetime reference for owned type实现一个 trait 方法,为拥有的类型返回一个有界的生命周期引用
【发布时间】:2021-01-28 19:20:24
【问题描述】:

假设我有这个结构和这个特征:

#[derive(Debug)]
pub struct New<T>(T);

pub trait AsRefNew<'a> {
    fn as_ref(&self) -> New<&'a str>;
}

也就是说,AsRefNew 特征允许返回具有给定生命周期的引用 'a 包装在 New 新类型中。此生命周期 'a 可能与 &amp;self 参数的生命周期不同(并且将会不同)。

现在我可以为New(&amp;str) 实现此特征,并使其输出的生命周期是包装的&amp;str 的生命周期:

impl<'a> AsRefNew<'a> for New<&'a str> {
    fn as_ref(&self) -> New<&'a str>{
        New(self.0)
    }
}

我的问题是我想为New(String) 实现特征,而这一次,我希望'a 实际匹配self 的生命周期。我的理解是这样的事情应该有效:

impl<'a> AsRefNew<'a> for New<String> where Self: 'a{
    fn as_ref(&self) -> New<&'a str> {
        New(self.0.as_str())
    }
}

除非它没有:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/main.rs:16:20
   |
16 |         New(self.0.as_str())
   |                    ^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 15:5...
  --> src/main.rs:15:5
   |
15 |     fn as_ref(&self) -> New<&'a str> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:16:13
   |
16 |         New(self.0.as_str())
   |             ^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 14:6...
  --> src/main.rs:14:6
   |
14 | impl<'a> AsRefNew<'a> for New<String> where Self: 'a{
   |      ^^
note: ...so that the expression is assignable
  --> src/main.rs:16:9
   |
16 |         New(self.0.as_str())
   |         ^^^^^^^^^^^^^^^^^^^^
   = note: expected `New<&'a str>`
              found `New<&str>`

我尝试了生命周期和泛型的不同变体,但我找不到更好的方式来表达我在这种情况下希望'a 匹配'_

目标是让这个 sn-p 工作:

fn main() {
    // This works:
    let a = String::from("Hey");
    let b;
    {
        let c = New(a.as_str());
        b = c.as_ref().0;
    }
    println!("{:?}", b);
    
    // I would like that to work as well:
    let a = String::from("Ho");
    let b;
    let c = New(a);
    {
        b = c.as_ref().0;
    }
    println!("{:?}", b);
}

有什么想法吗?

【问题讨论】:

  • 我不确定这个问题是否可以回答,直到您详细说明“此生命周期 'a 可能与 &amp;self 参数的生命周期不同(并且将会)”并提供一些具体示例.请编辑您的问题以使用其他澄清细节对其进行更新。鉴于您的问题,我是able to come up with this example,但我不确定它是否真的解决了您的问题。
  • 感谢您的浏览!我添加了一个示例来展示我想要的内容!所以我真的需要New&lt;String&gt; 的实现,而不是New&lt;&amp;String&gt;

标签: rust traits lifetime borrowing


【解决方案1】:

正如 Sven 所解释的,为了使这项工作发挥作用,我们需要两个不同的方法原型,而 AsRefNew 定义的 trait 无法做到这一点。

仍然可以修改它以使小 sn-p 工作,例如在签名中引入第二个生命周期:

#[derive(Debug)]
pub struct New<T>(T);

pub trait AsRefNew<'b, 'a> {
    fn as_ref(&'b self) -> New<&'a str>;
}

impl<'a> AsRefNew<'_, 'a> for New<&'a str> {
    fn as_ref(&self) -> New<&'a str>{
        New(self.0)
    }
}

impl<'b, 'a> AsRefNew<'b, 'a> for New<String> where 'b:'a {
    fn as_ref(&'b self) -> New<&'a str> {
        New(self.0.as_str())
    }
}

impl<T> New<T> {
    pub fn test<'b, 'a>(&'b self) -> New<&'a str> where Self: AsRefNew<'b, 'a> {
        self.as_ref()
    }
}

下面的 sn-p 现在可以工作了:

fn main() {
    // This works:
    let a = String::from("Hey");
    let b;
    {
        let c = New(a.as_str());
        b = c.as_ref().0;
    }
    println!("{:?}", b);
    
    // It now works
    let a = String::from("Ho");
    let b;
    let c = New(a);
    {
        b = c.as_ref().0;
    }
    println!("{:?}", b);
}

New 类型上的方法的通用实现也是如此:

impl<T> New<T> {
    pub fn test<'b, 'a>(&'b self) -> New<&'a str> where Self: AsRefNew<'b, 'a> {
        self.as_ref()
    }
}

现在唯一的问题是签名超级难看!我想知道这是否可以通过 gats 变得更简单。

【讨论】:

    【解决方案2】:

    这是不可能的。 Trait 方法只能有一个原型,并且所有实现都必须匹配该原型。

    对于New&lt;&amp;'a str&gt; 的案例,您的原型必须是您所拥有的

    fn as_ref(&self) -> New<&'a str>;
    

    另一方面,对于New&lt;String&gt; 的情况,您需要

    fn as_ref<'b>(&'b self) -> New<&'b str>;
    

    相反,即返回值的生命周期需要与self 引用的生命周期相关联。这个原型是不同的,因为生命周期是原型的一部分。

    您尝试使用 trait bound Self: 'a 解决这个问题是行不通的。 Self 的类型在这里是 New&lt;String&gt;,它不包含任何引用,所以它实际上很容易满足 Self: 'static,因此是任何生命周期的约束。该界限不会以任何方式进一步限制Self 类型。您不能在 impl 级别限制 self 引用的生命周期,并且在函数定义级别限制它会导致与 trait 定义中的原型不同,如上所述。

    出于同样的原因,您不能通过取消引用从Cow&lt;'a, str&gt; 中获得&amp;'a strCow 可能是拥有的,在这种情况下,返回的引用只能与用于取消引用的 self 引用一样长。

    【讨论】:

    • 太伤心了!是的,我认为添加边界可能会以某种方式暗示编译器理解&amp;self 真的是&amp;'a self,但它肯定不起作用。关于如何使我的 sn-p 工作的任何其他想法?我找不到任何其他解决方案,但可能有一个!
    • 让它发挥作用真的没有意义。在你使用 trait 的地方,例如在带有T: AsRefNew&lt;'a&gt; 的泛型代码中,编译器需要能够在不知道T 的实际类型的情况下确定as_ref() 返回的引用的生命周期,否则就不可能借用检查泛型代码.
    • 是的,这正是我想要通过该特征实现的目标。但是由于您的评论,我现在明白我希望方法原型在实现之间以某种方式更改,但这确实是不可能的。
    • 刚刚找到了一种通过修改签名让它工作的方法,但是现在超级难看。不过感谢您的反馈 ;)
    猜你喜欢
    • 2015-11-01
    • 1970-01-01
    • 2021-05-12
    • 1970-01-01
    • 1970-01-01
    • 2019-10-31
    • 2021-09-08
    • 2019-12-18
    • 2019-12-27
    相关资源
    最近更新 更多