# Pre-RFC: Expand object safety

**URL:** https://internals.rust-lang.org/t/pre-rfc-expand-object-safety/12693
**Category:** Uncategorized
**Created:** [July 8, 2020, 12:45pm UTC](https://internals.rust-lang.org/t/pre-rfc-expand-object-safety/12693 "2020-07-08T12:45:26Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![PoignardAzur](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/poignardazur/32/6464_2.png) [@PoignardAzur](https://internals.rust-lang.org/u/PoignardAzur)
#### Post date: [July 8, 2020, 12:45pm UTC](https://internals.rust-lang.org/t/pre-rfc-expand-object-safety/12693/1 "2020-07-08T12:45:27Z")

</div>

This is a draft for a potential MCP I had in mind for a while. I don't really intend to champion it any time soon, especially since I already have a MCP in the pipeline. I'm posting this in case this inspires anyone, and for future reference in dyn-safety discussions. Feedback welcome.

# Proposal

**NOTE:** There has been some debate in the past as to what term best describes when a trait can be turned into a trait object with the syntax `dyn MyTrait`. The current official term is "object safety", but it is widely agreed to be unsatisfying. We use "dyn-safety" in this document.

## Summary

- Progressively move the language towards having by-default dyn-safety for all traits.

- To that end, add a new `?dyn` trait qualifier to indicate that template parameters accept a trait object.

- Loosen dyn-safety restrictions. Allow traits with associated functions, associated constants and generic methods to be made into trait objects (with some caveats related to `?dyn`).

- Plan a long-term roadmap for more restrictions to be removed over time, so that eventually `impl SomeTrait` and `dyn SomeTrait` in function arguments are functionally equivalent.

## Motivation

There are frequent requests to make Rust friendlier towards runtime polymorphism, aka vtables. Use cases include:

- [Dynamic linking of Rust libraries](https://internals.rust-lang.org/t/a-plugin-system-for-business-applications/12313/).
- [Polymorphisation of generic functions](https://gankra.github.io/blah/swift-abi/#polymorphic-generics) (eg compile a generic function once with existential types, and re-use that version every time the function is instanced).
- Reducing code size for low-memory systems.

On the other hand, in current Rust, a given trait `MyTrait` can only be used dynamically if it is dyn-safe; specifically, if it obeys [a specific set of rules](https://huonw.github.io/blog/2015/01/object-safety/). The aim of these rules is to allow developers to pass compile-time implementations and trait objects interchangeably to any interface specying that it wants an instance of `MyTrait`.

This proposal claims that a different set of rules should be adopted, that still allows trait-objects and compile-time types to be used interchangeably, while expanding the scope of traits that can be used as trait objects.

The new set of rules would be:

- For each trait, internally compute a dyn-safe subset of that trait. For instance, that subset would exclude associated functions and associated constants.

- Add a `?dyn` trait qualifier to template parameters. Possible syntax:

- Allow trait objects to be created for traits with associated functions and associated constants.

- Allow trait objects to have generic methods and be dyn-safe, _if_ every generic type argument is bound to a dyn-safe trait or the dyn-safe subset of a trait. Example:

This new set of rules would allow trait objects to be made for a wider set of traits (traits with associated functions/constants, traits with generic methods) with no modification of existing code. In particular, this would allow developers to use traits in existing libraries that weren't designed with trait objects in mind, in contexts where trait objects are necessary.

**NOTE:** This proposal deliberately doesn't mention `Sized`. It's written with the assumption that, if/when RFC-1909 and RFC-2884 are implemented, size will matter less to the language over time, and syntax will eventually be changed accordingly. Strictly speaking, the examples would probably need `+ ?Sized` annotations to compile, barring an edition change.

## Breaking changes

None that I can see.

This feature should be strictly additive.

## Future improvements

- Have `impl MyTrait` arguments use the dyn-safe subset of MyTrait by default (breaking change).

- Add a syntax to access associated functions/constants from a trait object (eg `<typeof my_trait_object>::static_method(...)`); this would require that additional data be stored in the vtable.

- Perform the step above automatically in cases where the relevant vtable can be trivially found from an argument.

---

<div class="post-metadata">

### Author: ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)
#### Post date: [July 8, 2020, 7:14pm UTC](https://internals.rust-lang.org/t/pre-rfc-expand-object-safety/12693/2 "2020-07-08T19:14:10Z")

</div>

> [@PoignardAzur](#):
>
> - If the `?dyn` qualifier isn't used, the current rules apply.
> - If the `?dyn` qualifer is used, `my_func` is only allowed to use the dyn-safe subset of MyTrait.

My one largest fear is that makes the concept of a "completely dyn-compatible trait" more subtly problematic. Because now instead of `dyn Trait` being an error for traits that aren't, whether `fn foo(impl Trait); foo(&x as &dyn Trait)` works is not based on whether `dyn Trait` works but on whether `Trait` is "completely dyn-compatible". (Or otherwise stated, whether adding `?dyn` is a no-op.)

My secondary fear is smaller, but that basically every generic argument is going to be `?dyn`. As the set of things allowed under `?dyn` grows, the reason to _not_ use `?dyn` shrinks, so it will eventually end up just a required sigil for most use cases (that can still be omitted and then just make the API less usable).

But this is also effectively what best practices are today, just that you separate out the dyn-safe part of the trait manually. For that reason I think this is probably an improvement and motion in the correct direction, and I fail to see an edition-compatible way to make the default not be "wrong." (Except _maybe_ `<T1: ?dyn Trait, T2: impl Trait>`, but that also seems strictly worse.)

---

<div class="post-metadata">

### Author: ![Manishearth](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/manishearth/32/50_2.png) [@Manishearth](https://internals.rust-lang.org/u/Manishearth)
#### Post date: [September 19, 2020, 6:23pm UTC](https://internals.rust-lang.org/t/pre-rfc-expand-object-safety/12693/3 "2020-09-19T18:23:54Z")

</div>

> [@PoignardAzur](#):
>
> For each trait, internally compute a dyn-safe subset of that trait. For instance, that subset would exclude associated functions and associated constants.

So, a lot of people aren't aware of this, but you can make this work manually already, you can "opt out" of associated items being included on trait objects with `where Self: Sized`. I personally vastly prefer this: object safety rules are typically not on the forefront of my mind so when reading code it is easier if I can see that an associated item doesn't work on the trait object instead of having to check if something is object safe. And I _wrote_ the docs on the different reasons a method may not be object safe.

Furthermore, note that this is infectuous: any method returning an associated item, any method _using_ an associated item in its defaulted body, all need to have `where Self: Sized` as well, which means the reason why a method is inaccessible on a trait object may not be immediately obvious.

Every time I've had to make a trait object safe, there has been a cascade of changes (usually `where Self: Sized`s) that is necessary to make it work. It is hard for me to see how these changes can be implicitly made by the compiler in all but the simplest case, and it's hard for me to see how implicit `where Self: Sized` will not be confusing for people.

> [@PoignardAzur](#):
>
> Add a `?dyn` trait qualifier to template parameters. Possible syntax:

I do not see how this is any different from `T: ?Sized + MyTrait`. You mention later that not mentioning `Sized` is deliberate, but I don't see the point of `?dyn` here aside from perhaps distinguishing object safety issues from unsized type issues.

Furthermore, `?dyn` as a term is confusing: `?Sized` means "may not be Sized (the default)", whereas `?dyn` means "may be `dyn` (not the default)". The `?` means different things here.

> [@PoignardAzur](#):
>
> Allow trait objects to have generic methods and be dyn-safe, _if_ every generic type argument is bound to a dyn-safe trait or the dyn-safe subset of a trait. Example:

The reason generic methods are not object safe is not because of the trait bounds being not-object safe, it is because it can explode the vtable -- you now need a vtable entry for each debuggable type in your program.

---

<div class="post-metadata">

### Author: ![PoignardAzur](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/poignardazur/32/6464_2.png) [@PoignardAzur](https://internals.rust-lang.org/u/PoignardAzur)
#### Post date: [October 9, 2020, 9:38pm UTC](https://internals.rust-lang.org/t/pre-rfc-expand-object-safety/12693/4 "2020-10-09T21:38:17Z")

</div>

Late reply:

> [@CAD97](#):
>
> My secondary fear is smaller, but that basically every generic argument is going to be `?dyn` . As the set of things allowed under `?dyn` grows, the reason to _not_ use `?dyn` shrinks, so it will eventually end up just a required sigil for most use cases (that can still be omitted and then just make the API less usable).

That's the plan, really. Ideally we'd get to a point where `?dyn` is the default and non-dyn-compatible generics are the exception that require an annotation. (with the benefits in dynamic linking, compile times, etc, this entails)

> [@Manishearth](#):
>
> So, a lot of people aren't aware of this, but you can make this work manually already, you can "opt out" of associated items being included on trait objects with `where Self: Sized` .

My understanding is that you can't put this bond on associated consts, though.

But yeah, I did some tests in the playground and that workaround is more powerful than I thought.

> [@Manishearth](#):
>
> The reason generic methods are not object safe is not because of the trait bounds being not-object safe, it is because it can explode the vtable -- you now need a vtable entry for each debuggable type in your program.

In this case, a single vtable entry would be generated, that would itself use the `std::fmt::Debug` vtable of the passed object.

The vtable would essentially be used similarly to Swift's witness tables.

---

<div class="post-metadata">

### Author: ![Manishearth](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/manishearth/32/50_2.png) [@Manishearth](https://internals.rust-lang.org/u/Manishearth)
#### Post date: [October 9, 2020, 11:23pm UTC](https://internals.rust-lang.org/t/pre-rfc-expand-object-safety/12693/5 "2020-10-09T23:23:04Z")

</div>

> [@PoignardAzur](#):
>
> My understanding is that you can't put this bond on associated consts, though.

That seems to me to be the stronger fix for most of the use cases of this RFC.

> [@PoignardAzur](#):
>
> In this case, a single vtable entry would be generated, that would itself use the `std::fmt::Debug` vtable of the passed object.

That's going to force a `?Sized` on those as well, it's going to be infectuous. Also, this cannot handle _multiple_ generics that mix particularly well.

---

<div class="post-metadata">

### Author: ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)
#### Post date: [January 7, 2021, 11:23pm UTC](https://internals.rust-lang.org/t/pre-rfc-expand-object-safety/12693/6 "2021-01-07T23:23:35Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
