Does object safety prevent any legitimate use cases?

https://github.com/rust-lang/rfcs/blob/master/text/0255-object-safety.md

This RFC is mentioned in the trait objects chapter of the Rust book. It seems reasonable from a technical perspective.

I'm curious why there'd be any opposition. I can't think of any case where I'd want to violate this restriction. And that's even coming from Ruby, which is super dynamic. Is there anything that object safety prevents that wouldn't otherwise be a code smell?

My main issue with the existing trait objects implementation is that it's impossible to use self-consuming methods on them (i.e. fn finalize(self) -> Foo). And I don't need full ownership over self, as inside of those methods I only need &mut self, but I want type system to prevent object usage after its finalization.

3 Likes

Thank you for the reply. Seeing examples helps me start to understand.

Dynamic dispatch is clear enough, but I found this notion of object safety very curious.

My main issue with the existing trait objects implementation is that it’s impossible to use self-consuming methods on them (i.e. fn finalize(self) -> Foo).

What do you expect to happen to the container holding the trait object (i.e. Box, Rc) after you call finalize if the object in it is no longer valid? Perhaps what you are looking for is fn finalize(self: Box<Self>) -> Foo (which is legal).

1 Like

This works well when you have a trait object via a Box, but doesn’t for other flavors (eg Rc, Arc, &, &mut). Arbitrary self types would help some with the Rc/Arc variants, but it still leaves the reference forms hanging.

As for what happens to the underlying object, it should be no different than what happens with them in normal invocations when the value is moved (containers destruct, references just move or copy but leave the owned value alone).

1 Like

I've implied containers which exclusively own an underlying data (i.e. Box, but not Rc), for them self-consuming method will be equivalent to the usual moving function call. fn finalize(self: Box<Self>) not the best solution because now method can not be used on the non-boxed type (which leads to methods duplication), there is workaround which uses impl Trait for Box<Trait>, but it's not that ergonomic and can lead to code duplication.

1 Like

Note that among other things, object safety prohibits any methods that have generic parameters. From an implementation perspective, the reason for this is pretty clear (Rust currently always monomorphizes generics, and you can't monomorphize at runtime), but it makes trait objects feel quite limited, given that idiomatic Rust is pretty heavy on generics.

1 Like