Let's assume I have this struct and this trait:
#[derive(Debug)]
pub struct New<T>(T);
pub trait AsRefNew<'a> {
fn as_ref(&self) -> New<&'a str>;
}
That is, the AsRefNew
trait allows to return a reference with a given lifetime 'a
wrapped in a New
newtype. This lifetime 'a
may be different (and will be) from the lifetime of the &self
parameter.
Now I can implement this trait for a New(&str)
, and make it so that the lifetime of the output is the lifetime of the wrapped &str
:
impl<'a> AsRefNew<'a> for New<&'a str> {
fn as_ref(&self) -> New<&'a str>{
New(self.0)
}
}
My problem is that I would like to implement the trait for New(String)
, and this time, I would like 'a
to actually match the lifetime of self
. My understanding is that something like that should work:
impl<'a> AsRefNew<'a> for New<String> where Self: 'a{
fn as_ref(&self) -> New<&'a str> {
New(self.0.as_str())
}
}
Except it does not:
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>`
I tried different variations of lifetimes and generic, but I can not find a better way to express the fact that I, in this case, want 'a
to match '_
.
The goal is to have this snippet to work:
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);
}
Any ideas?
As explained by Sven, in order to make that work, we would need two different method prototypes, and that is not possible with the AsRefNew
trait as it is defined.
Still, it can be modified to make the small snippet work by, eg introducing a second lifetime in the signature:
#[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()
}
}
The following snippet now works:
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);
}
And so is this generic implementation of a method on the New
type:
impl<T> New<T> {
pub fn test<'b, 'a>(&'b self) -> New<&'a str> where Self: AsRefNew<'b, 'a> {
self.as_ref()
}
}
The only problem now is that the signature is super ugly. I wonder whether this could made simpler with gats.
This isn't really possible. Trait methods can only have a single prototype, and all implementations have to match that prototype.
For the case New<&'a str>
, your prototype needs to be what you have
fn as_ref(&self) -> New<&'a str>;
For the case New<String>
, on the other hand, you would need
fn as_ref<'b>(&'b self) -> New<&'b str>;
instead, ie the lifetime of the return value needs to be tied to the lifetime of the self
reference. This prototype is different, since lifetimes are part of the prototype.
Your attempt to solve this with a trait bound Self: 'a
can't work. The type Self
is New<String>
here, which does not contain any references, so it in fact trivially fulfils Self: 'static
, and thus any lifetime bound. The bound does not further constrain the Self
type in any way. You can't restrict the lifetime of the self
reference at the impl level, and bounding it at the function definition level will result in diverging from the prototype in the trait definition, as explained above.
For the same reason, you can't get an &'a str
from a Cow<'a, str>
by dereferencing. The Cow
might be owned, in which case the returned reference can only live as long a the self
reference used for dereferencing.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.