Idea: ?Sized for impl &T, impl &mut T

Example:

This: fn f(a: &impl Trait)

Now desugared as: fn f<T:Trait>(a: &T)

But it should be: fn f<T:Trait + ?Sized>(a: &T)

Same for &mut T.

Compatibility: Since T declaration is implicit and can't be used inside fn this change doesn't break anything.

Motivation: This change allows passing &dyn and &mut dyn directly to such methods. T becomes dyn Trait, &T becomes &dyn Trait - though T has dynamic size the parameter &T is a fixed size type so everything is fine.

1 Like

Sadly this is not true :slightly_frowning_face:. Consider:

fn foo (a: &impl Trait)
{
    bar(a);
}

fn bar<T : Trait> (a: &T)
{}
  • If the definition of foo were to change and accept ?Sized types / implementors, then it would no longer be able to forward a call to bar.
7 Likes

Hm, you are right. And if such method is declared in trait the compiler can't even check the code for such things and remove ?Sized. May be this improvement can be put behind the next "language version".

The language doesn't need to change- you can already opt into unsized impl Trait types like this:

fn foo(x: &(impl ?Sized + Trait)) {
    bar(x)
}

fn bar<T: ?Sized + Trait>(x: &T) {}
9 Likes

But that's just... not readable.

Changing the default would just make the other case "not readable:"

fn foo(x: &(impl Sized + Trait)) {
    bar(x)
}

fn bar<T: Trait>(x: &T) {}
// Or perhaps even this, for consistency:
fn bar<T: Sized + Trait>(x: &T) {}

The choice was made based on which case is more common. Having a weird "unreadable" syntax for an uncommon case is better than that case not being possible at all, and it's certainly better than having the common case be "unreadable."

4 Likes