Why can't Weak::new() be used with a trait?

I want to have a weak pointer that can point to various values that implement a trait. A variable declared as let mut weak_ptr: Weak<dyn MyTrait> works for this. Let’s say I want to first initialize it to be an empty weak pointer. The following do not work:

//let mut weak_ptr: Weak<dyn MyTrait> = Weak::new(); // error: cannot infer type for `T`
//let mut weak_ptr: Weak<dyn MyTrait> = Weak::<dyn MyTrait>::new(); // error: function or associated item not found in `std::rc::Weak<dyn main::MyTrait>`
//let mut weak_ptr: Weak<dyn MyTrait> = Default::default(); // error: doesn't have a size known at compile-time

Explicitly specifying the generic type argument as some struct that implements the trait works:

struct ArbitraryStructThatImplTrait;
impl MyTrait for ArbitraryStructThatImplTrait { }
let mut weak_ptr: Weak<dyn MyTrait> = Weak::<ArbitraryStructThatImplTrait>::new()

I believe this is because both Weak::new() and Weak::default() are declared with impl<T> without where T: ?Sized, so the type argument cannot be a dynamically sized type like a trait object.

However, I see no reason why Weak::new() and Weak::default() can’t work for dynamically-sized types too. Empty weak pointers of any type work the same way, and the implementation of Weak::new() simply casts the max integer to a pointer – it doesn’t store anything that depends on the type T. I can already create an empty weak pointer of a struct that implements the trait, and assign that to a weak pointer of the trait, so I don’t see why I shouldn’t be able to create the empty weak pointer of the trait directly.

1 Like

Fat pointer metadata must be always valid even if the pointer is invalid, which means you can’t generate the invalid vtables needed for Weak::new.

Hmm… that seems kinda silly as an empty weak pointer will never use a vtable of T. Could this be made to work if Weak were internally implemented with an Option, with empty pointer being None?

Yes, but then you would lose the non-null optimization and pay that performance penalty on every use even if that cost is unnecessary. You can wrap it in Option instead, and use None to represent not initialized

The think reason for this has to do with CustomDsts. @RalfJung should know more about why pointer metadata should always be valid even if the pointer is invalid.

1 Like

This is an interesting question! I think this is the first use-case I see for a fat reference (not a raw pointer) to carry “bogus” metadata. The reason we require it to be valid is mostly that when i doubt, it seems produent to have a stronger invariant.

I opened a UCG issue for this.