`Sized` bound on GAT stops code from compiling

Following code compiles (playground):

trait Borrow {
    type Borrowed<'a>
    where
        Self: 'a;

    fn borrow(&self) -> Self::Borrowed<'_>;
}

enum Cow<'a, B: Borrow + 'a> {
    Borrowed(B::Borrowed<'a>),
    Owned(B),
}

impl<'cow, B: Borrow + 'cow> Borrow for Cow<'cow, B>
// where
//     B::Borrowed<'cow>: Sized,
{
    type Borrowed<'a> = B::Borrowed<'a> where Self: 'a;

    fn borrow(&self) -> B::Borrowed<'_> {
        todo!()
    }
}

But if we uncomment the two commented lines, it doesn't compile (playground).

Why does the Sized bound matter here?

You'll need a HRTB:
Rust Playground

Also note how to give a lifetime parameter a meaningful name.


Sized is irrelevant here [1], since

All type parameters have an implicit bound of Sized, except the implicit Self type of a trait.


  1. which means you can remove the Sized bound here ↩︎

1 Like

Thanks for the HRTB solution!

But I feel Sized is somehow relavant here because if we change Sized to std::fmt::Debug it compiles.

Playground

I advise you read your error message twice:

error[E0308]: mismatched types
  --> src/lib.rs:18:25
   |
18 |     type Borrowed<'a> = B::Borrowed<'a> where Self: 'a;
   |                         ^^^^^^^^^^^^^^^ lifetime mismatch
   |
   = note: expected trait `<<B as Borrow>::Borrowed<'a> as Sized>`
              found trait `<<B as Borrow>::Borrowed<'cow> as Sized>`
note: the lifetime `'a` as defined here...
  --> src/lib.rs:18:19
   |
18 |     type Borrowed<'a> = B::Borrowed<'a> where Self: 'a;
   |                   ^^
note: ...does not necessarily outlive the lifetime `'cow` as defined here
  --> src/lib.rs:14:6
   |
14 | impl<'cow, B: Borrow + 'cow> Borrow for Cow<'cow, B>
   |      ^^^^

where did it say about Sized ? It's all about the lifetimes.

Yes the error message doesn't say anything about Sized. I'm just wondering what's the difference here between Sized bound and std::fmt::Debug bound. After all, one code compiles and the other doesn't.

And it's not just about Sized and std::fmt::Debug. Clone and Copy doesn't compile. AsRef<u8> and 'static compiles. Seems like if the trait is not object safe, code doesn't compile. If the trait is object safe, code compiles.

I feel something deeper is going on here.

1 Like

Clone and Copy requires Sized. That's probably why.

And it's not about object safety, as the following compiles ok.

trait NotObjectSafe {
    fn foo(self);
}

impl<'cow, B: Borrow + 'cow> Borrow for Cow<'cow, B>
where
    B::Borrowed<'cow>: NotObjectSafe,
//....

I have no idea why Sized bound breaks the code though.

2 Likes

Minimized:

trait Borrow {
    type Borrowed<'a>;
}

impl<'cow, T: Borrow + 'cow> Borrow for (T,)
where
    T::Borrowed<'cow>: Sized,  // Add this line cause error
{
    type Borrowed<'a> = T::Borrowed<'a>; 
}
1 Like

A version without the Borrow noise (playground):

trait Trait {
    type Associated<'a>;
}

impl<'t, T: Trait + 't> Trait for (T,)
where
    T::Associated<'t>: Sized,  // Add this line cause error
{
    type Associated<'a> = T::Associated<'a>; 
}

FYI (might be helpful)

You can open a new issue in Rust-lang repo.

3 Likes

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.