Generic implementations of traits vs. lifetimes

Consider the following code:

trait AsBytes {
    fn as_bytes(&self) -> &[u8];
}

impl AsBytes for [u8] {
    fn as_bytes(&self) -> &[u8] {
        self
    }
}
impl AsBytes for str {
    fn as_bytes(&self) -> &[u8] {
        self.as_ref()
    }
}
impl AsBytes for String {
    fn as_bytes(&self) -> &[u8] {
        self.as_ref()
    }
}
impl<T: ?Sized> AsBytes for &T
where
    T: AsBytes,
{
    fn as_bytes(&self) -> &[u8] {
        <T as AsBytes>::as_bytes(*self)
    }
}

fn foo(s: &[u8]) -> impl Iterator<Item=&[u8]> + '_ {
    s.as_bytes().split(|&b| b == b' ')
}

Playground

This builds just fine.

Now, if you try to write the same thing with a more generic twist, it fails:

trait AsBytes {
    fn as_bytes(&self) -> &[u8];
}

impl<T: AsRef<[u8]>> AsBytes for T {
    fn as_bytes(&self) -> &[u8] {
        self.as_ref()
    }
}

fn foo(s: &[u8]) -> impl Iterator<Item=&[u8]> + '_ {
    s.as_bytes().split(|&b| b == b' ')
}

Playground

The failure is:

error[E0515]: cannot return value referencing function parameter `s`
  --> src/lib.rs:12:5
   |
12 |     s.as_bytes().split(|&b| b == b' ')
   |     -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |     |
   |     returns a value referencing data owned by the current function
   |     `s` is borrowed here
   |
   = help: use `.collect()` to allocate the iterator

I'm rather lost as to why there is a difference.

The difference is whether it's implemented for [u8] or &[u8]. This will cause a similar error:

impl<'long> AsBytes for &'long [u8] {
    fn as_bytes<'short>(&'short self) -> &'short [u8] {
        *self
    }
}
1 Like

The problem when AsBytes is not defined for [u8] is that a temporary reference is taken so that the implementation for &[u8] can be called instead.

The reason that AsBytes was not defined for [u8] is that type parameters have an implicit Sized bound, and [u8] is not Sized. You can remove the bound like so:

-impl<T: AsRef<[u8]>> AsBytes for T {
+impl<T: ?Sized + AsRef<[u8]>> AsBytes for T {
     fn as_bytes(&self) -> &[u8] {
         self.as_ref()
     }
 }

Playground.

3 Likes

Aaaah ?Sized, I completely missed that one. Thanks.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.