Implement a trait for any lifetime &str

Is it possible to implement a trait for any lifetime of &str when the trait has functions returning Self?

The closest solution that I have been able to get is the following:

use std::str;
use std::fmt::Display;

pub trait Deserializer<'a>: Sized {
    type Error: Display;

    fn deserialize(_: &'a [u8]) -> Result<Self, <Self as Deserializer>::Error>;
}

impl<'a: 's, 's> Deserializer<'a> for &'s str {
    type Error = str::Utf8Error;

                                           // ↓ returns &str
    fn deserialize(bytes: &'a [u8]) -> Result<Self, <Self as Deserializer>::Error> {
        str::from_utf8(bytes)
    }
}


fn test<'a, D, F>(mut f: F) -> Result<(), <D as Deserializer<'a>>::Error>
where
    F: FnMut(D),
    D: Deserializer<'a>,
{
    let xs: Vec<u8> = vec![0, 1];
    f(D::deserialize(&xs[..])?);
    Ok(())
}

fn main() {
    test(|_: &str| ()).unwrap();
}

In the playground

The compiler rejects the code because xs is expected to have a different lifetime:

error[E0597]: `xs` does not live long enough
  --> 1.rs:25:23
   |
19 | fn test<'a, D, F>(mut f: F) -> Result<(), <D as Deserializer<'a>>::Error>
   |         -- lifetime `'a` defined here
...
25 |     f(D::deserialize(&xs[..])?);
   |       ----------------^^-----
   |       |               |
   |       |               borrowed value does not live long enough
   |       argument requires that `xs` is borrowed for `'a`
26 |     Ok(())
27 | }
   | - `xs` dropped here while still borrowed

error: aborting due to previous error

For more information about this error, try `rustc --explain E0597`.

How can I tell the compiler that the lifetime of the deserialize result is the same lifetime of its argument?

Trait with no lifetimes

Without lifetime parameters:

pub trait Deserializer: Sized {
    type Error: Display;

    fn deserialize(_: &[u8]) -> Result<Self, <Self as Deserializer>::Error>;
}

impl Deserializer for &str {
    type Error = str::Utf8Error;

    fn deserialize(bytes: &[u8]) -> Result<Self, <Self as Deserializer>::Error> {
        str::from_utf8(bytes)
    }
}

Shows a different error:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> 2.rs:14:9
   |
14 |         str::from_utf8(bytes)
   |         ^^^^^^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 13:5...
  --> 2.rs:13:5
   |
13 | /     fn deserialize(bytes: &[u8]) -> Result<Self, <Self as Deserializer>::Error> {
14 | |         str::from_utf8(bytes)
15 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> 2.rs:14:24
   |
14 |         str::from_utf8(bytes)
   |                        ^^^^^
note: but, the lifetime must be valid for the lifetime `'_` as defined on the impl at 10:23...
  --> 2.rs:10:23
   |
10 | impl Deserializer for &str {
   |                       ^
note: ...so that the expression is assignable
  --> 2.rs:14:9
   |
14 |         str::from_utf8(bytes)
   |         ^^^^^^^^^^^^^^^^^^^^^
   = note: expected  `std::result::Result<&str, _>`
              found  `std::result::Result<&str, _>`

Using unsafe

As an exercise, I tried mem::transmute, and it works (playground).

pub trait Deserializer: Sized {
    type Error: Display;

    fn deserialize(_: &[u8]) -> Result<Self, <Self as Deserializer>::Error>;
}

impl Deserializer for &str {
    type Error = str::Utf8Error;

    fn deserialize(bytes: &[u8]) -> Result<Self, <Self as Deserializer>::Error> {
        unsafe { std::mem::transmute(str::from_utf8(bytes)) }
    }
}

You can't, as that would violate Rust's safety guarantees. You must use the same lifetime for the input slice as the &str. This is Rust's compiler errors saving you from a use-after-free, and your unsafe code is unsound.

You can fix your playground example by moving the buffer outside.

2 Likes

Thanks.

I changed the code using the deserializer to be able to implement it as impl<'a> Deserializer<'a> for &'a str.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.