问题描述
假设我有这个结构和这个特征:
#[derive(Debug)]
pub struct New<T>(T);
pub trait AsRefNew<'a> {
fn as_ref(&self) -> New<&'a str>;
}
也就是说,AsRefNew
trait 允许返回包含在 'a
新类型中的给定生命周期 New
的引用。此生命周期 'a
可能(并且将会)与 &self
参数的生命周期不同。
现在我可以为 New(&str)
实现这个特征,并使其输出的生命周期与包装的 &str
的生命周期相同:
impl<'a> AsRefNew<'a> for New<&'a str> {
fn as_ref(&self) -> New<&'a str>{
New(self.0)
}
}
我的问题是我想为 New(String)
实现 trait,而这一次,我希望 '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
与 '_
匹配这一事实。
目标是让这个代码片段起作用:
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);
}
有什么想法吗?
解决方法
正如 Sven 所解释的那样,为了使这项工作发挥作用,我们需要两个不同的方法原型,而这对于定义的 AsRefNew
特征是不可能的。
仍然可以修改它以使小片段工作,例如在签名中引入第二个生命周期:
#[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()
}
}
以下代码段现在有效:
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> {
self.as_ref()
}
}
现在唯一的问题就是签名超级难看!我想知道 gats 是否可以使这更简单。
,这真的不可能。 Trait 方法只能有一个原型,所有实现都必须匹配该原型。
对于 New<&'a str>
的情况,您的原型必须是您所拥有的
fn as_ref(&self) -> New<&'a str>;
另一方面,对于 New<String>
的情况,您需要
fn as_ref<'b>(&'b self) -> New<&'b str>;
相反,即返回值的生命周期需要与 self
引用的生命周期相关联。这个原型是不同的,因为生命周期是原型的一部分。
您尝试使用 trait bound Self: 'a
来解决这个问题是行不通的。类型 Self
在这里是 New<String>
,它不包含任何引用,因此它实际上很简单地满足了 Self: 'static
,因此它满足了任何生命周期限制。边界不会以任何方式进一步限制 Self
类型。您不能在 impl 级别限制 self
引用的生命周期,并且在函数定义级别对其进行限制将导致与 trait 定义中的原型背道而驰,如上所述。
出于同样的原因,您无法通过取消引用从 &'a str
获取 Cow<'a,str>
。可能拥有 Cow
,在这种情况下,返回的引用只能与用于取消引用的 self
引用一样长。