# Add \`#\[diagnostic::not\_object\_safe\]\`

**URL:** <https://internals.rust-lang.org/t/add-diagnostic-not-object-safe/20973>\
**Category:** language design\
**Created:** [June 4, 2024, 1:54pm UTC](https://internals.rust-lang.org/t/add-diagnostic-not-object-safe/20973 "2024-06-04T13:54:37Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![BD103](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bd103/32/12245_2.png) [@BD103](https://internals.rust-lang.org/u/BD103)\
**Post date:** [June 4, 2024, 1:54pm UTC](https://internals.rust-lang.org/t/add-diagnostic-not-object-safe/20973/1 "2024-06-04T13:54:37Z")

</div>

Hello! While writing part of the migration guide for Bevy 0.14, I stumbled across [this conversation](https://github.com/bevyengine/bevy/pull/12550#discussion_r1527677176). Since Rust does not yet support object-safe traits that use `-> impl Trait`, or in the case `async fn`, we've written a few type-erased versions. (E.g. [`AssetReader`](https://dev-docs.bevyengine.org/bevy/asset/io/trait.AssetReader.html) vs. [`ErasedAssetReader`](https://dev-docs.bevyengine.org/bevy/asset/io/trait.ErasedAssetReader.html))

While reviewing the pull request that implemented this, one of the contributors mentioned how it would be beneficial if we could add a hint directing people to use `dyn ErasedTrait` instead of `dyn Trait`, since the latter would fail to compile.

With the stabilization of the [`#[diagnostic]`](https://blog.rust-lang.org/2024/05/02/Rust-1.78.0.html#diagnostic-attributes) namespace, what if `#[diagnostic::not_object_safe]` was added in scenarios when a trait cannot be used with `dyn`? (Specifically when [E0038](https://doc.rust-lang.org/stable/error_codes/E0038.html) is raised.)

# Example

```rust
#[diagnostic::not_object_safe(
    message = "{Self} is not object safe.",
    note = "consider using `ErasedAssetReader` instead",
)]
pub trait AssetReader {
    // ...
}

pub trait ErasedAssetReader {
    // ...
}

let asset_reader: impl AssetReader = todo!();

// This would fail to compile, displaying the nice diagnostics from above.
let erased_asset_reader: &dyn AssetReader = &asset_reader;

// This would succeed.
let erased_asset_reader: &dyn ErasedAssetReader = &asset_reader;

```

# Naming

I'm not sure about the name `not_object_safe`. Please bikeshed ideas if you have them! Some of mine are:

- `not_object_safe`
- `not_dyn`
- ~~`on_dyn`~~ (Unclear)
- `on_invalid_dyn`
- `on_not_object_safe`
- ~~`on_object_unsafe`~~ ("unsafe" may be confusing)

# Requirements

Since this solves a specific problem, there are a few things that could be done to prevent it from being used incorrectly:

- Only allow annotating traits.
- Raise a warning if the trait **is** object safe, since the diagnostics would never be shown.

---

<div class="post-metadata">

**Author:** ![jjpe](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jjpe/32/9779_2.png) [@jjpe](https://internals.rust-lang.org/u/jjpe)\
**Post date:** [June 4, 2024, 2:21pm UTC](https://internals.rust-lang.org/t/add-diagnostic-not-object-safe/20973/2 "2024-06-04T14:21:22Z")

</div>

What should happen when the attribute is used on a trait that actually **is** object-safe?

The attribute could become a bit of a foot gun if it was usable on object-safe traits, because it would muddy the signal that the attribute is supposed to send. So it seems desirable then to disallow that.

---

<div class="post-metadata">

**Author:** ![BD103](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bd103/32/12245_2.png) [@BD103](https://internals.rust-lang.org/u/BD103)\
**Post date:** [June 4, 2024, 2:23pm UTC](https://internals.rust-lang.org/t/add-diagnostic-not-object-safe/20973/3 "2024-06-04T14:23:46Z")

</div>

> [@BD103](#):
>
> Raise a warning if the trait **is** object safe, since the diagnostics would never be shown.

It would raise a warning that the diagnostic can never be shown, but would still compile.

---

<div class="post-metadata">

**Author:** ![farnz](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/farnz/32/6587_2.png) [@farnz](https://internals.rust-lang.org/u/farnz)\
**Post date:** [June 4, 2024, 3:31pm UTC](https://internals.rust-lang.org/t/add-diagnostic-not-object-safe/20973/4 "2024-06-04T15:31:26Z")

</div>

> [@BD103](#):
>
> ```rust
> #[diagnostic::not_object_safe(
> message = "{Self} is not object safe.",
> note = "consider using `ErasedAssetReader` instead",
> )]
> 
> ```

For the case where you've got a type-erased version, would it be sensible to have special syntax? Something like:

```rust
#[diagnostic::not_object_safe(
    object_safe_alternative=ErasedAssetReader
)]

```

This would then allow the compiler to verify that `ErasedAssetReader` exists and to give you the right path to it (e.g. `bevy::asset::ErasedAssetReader`) given the `use` statements you have written so far.

Then, the `message` and `note` variant is for when there is no simple swap to make, pointing out alternative designs, while the `object_safe_alternative` variant expands to the message and note you've given (but with item paths etc correctly annotated).

---

<div class="post-metadata">

**Author:** ![BD103](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bd103/32/12245_2.png) [@BD103](https://internals.rust-lang.org/u/BD103)\
**Post date:** [June 4, 2024, 5:03pm UTC](https://internals.rust-lang.org/t/add-diagnostic-not-object-safe/20973/5 "2024-06-04T17:03:10Z")

</div>

That's an interesting idea, though I fear it may be more difficult to implement.

- Would you need to import the struct specified in `object_safe_alternative` before you use it?
- What if you also want to add messages / notes? Do they both work together?
- Should the object safe alternative be checked that it is, in fact, object safe?
- What if there is no object safe alternative? Is the field optional?

I do like the benefits of the compiler checking it for you, though.

---

<div class="post-metadata">

**Author:** ![farnz](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/farnz/32/6587_2.png) [@farnz](https://internals.rust-lang.org/u/farnz)\
**Post date:** [June 4, 2024, 6:31pm UTC](https://internals.rust-lang.org/t/add-diagnostic-not-object-safe/20973/6 "2024-06-04T18:31:44Z")

</div>

> [@BD103](#):
>
> - Would you need to import the struct specified in `object_safe_alternative` before you use it?

You'd need to supply a path to it that's valid from the current file. Saying `crate::asset::ErasedAssetLoader` would also be fine; the compiler is responsible for resolving paths to traits, and then back to paths in the final message, telling the user the fully qualified path to use, as it does for (e.g.) a [method whose trait is not currently in scope](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=68854b342330978badd8f81ac1f520af).

> [@BD103](#):
>
> - What if you also want to add messages / notes? Do they both work together?

Notes, yes, but not messages. If you use this, the message is always "{Self} is not object safe.", and there's a note "consider using `Thing` instead", where `Thing` is the item specified by the diagnostic.

But you should be able to add extra notes if it's not obvious how to translate between the two options, to make it easier on users - for example, you might use one to tell people the relationship between `bevy::asset::meta::Settings` and `bevy::asset::meta::AssetMetaDyn` to make it easier on users converting a `dyn AssetLoader` into `dyn ErasedAssetLoader`.

> [@BD103](#):
>
> - Should the object safe alternative be checked that it is, in fact, object safe?
> - What if there is no object safe alternative? Is the field optional?

It should be checked to see if it's object safe, and rejected if it's not - this is meant to point you at the right thing to resolve E0038, and it would be unhelpful at best if switching to the "right" thing resulted in a different E0038 instance.

The field is optional, so that you can use this for cases where you're documenting alternatives to object safety, instead of pointing you at an object-safe trait you can use. It can also be repeated, if there's more than one alternative that you could use with different trade-offs.

> [@BD103](#):
>
> That's an interesting idea, though I fear it may be more difficult to implement.

And yes, I do expect this to be a bit harder to implement - the advantage is that it stops you making mistakes, because the compiler (a) can semantically verify that the alternative you propose is object safe, and (b) can handle paths for you, so that you get the "right" import offered to users, not an internal-only path.

---

<div class="post-metadata">

**Author:** ![BD103](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bd103/32/12245_2.png) [@BD103](https://internals.rust-lang.org/u/BD103)\
**Post date:** [June 5, 2024, 9:36pm UTC](https://internals.rust-lang.org/t/add-diagnostic-not-object-safe/20973/7 "2024-06-05T21:36:41Z")

</div>

Your reasoning makes sense, I agree. Thanks for your help!

---

<div class="post-metadata">

**Author:** ![dhm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dhm/32/4879_2.png) [@dhm](https://internals.rust-lang.org/u/dhm)\
**Post date:** [June 11, 2024, 6:49pm UTC](https://internals.rust-lang.org/t/add-diagnostic-not-object-safe/20973/8 "2024-06-11T18:49:11Z")

</div>

I like the idea; mainly for suggesting a distinct dedicated `dyn`-safe trait.

- Speaking of which: the "object safe[ty]" terminology is kind of a legacy one from the beginning of Rust: Rust does not really have "objects", or if it did, users would not necessarily think of `dyn Trait`s as such. Nowadays the preferred term is `dyn`-safety\[1\] ([example](https://smallcultfollowing.com/babysteps/blog/2021/10/01/dyn-async-traits-part-2/#dyn-safety-a-mini-retrospective)). So a tiny bikeshedding nit w.r.t. the naming of the diagnostic tool would be to try and go in that direction 🙂

* * *

1. and even then, "safety" is a far-fetched term, from the point of view of users. I'd personally rather talk of _compatibility_, as in, being `dyn`-compatible, or just `dyn`-able. But official parlance does stick to safety, so let's do that as well.

---

<div class="post-metadata">

**Author:** ![binarycat](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/binarycat/32/12210_2.png) [@binarycat](https://internals.rust-lang.org/u/binarycat)\
**Post date:** [June 12, 2024, 2:54am UTC](https://internals.rust-lang.org/t/add-diagnostic-not-object-safe/20973/9 "2024-06-12T02:54:44Z")

</div>

> [@dhm](#):
>
> Rust does not really have "objects

i'm not sure what makes you think that, they're called "trait objects" and they use vtables just like C++ objects.

i would much prefer using established terms than pretending we've invented something completely new. for example, rust claims to not have try/catch, but catch\_unwind provides the same functionality.

soon rust will basically have full (multiple!) inheritance too, once we can downcast trait objects into their supertraits.

---

<div class="post-metadata">

**Author:** ![quinedot](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/quinedot/32/7294_2.png) [@quinedot](https://internals.rust-lang.org/u/quinedot)\
**Post date:** [June 12, 2024, 3:38am UTC](https://internals.rust-lang.org/t/add-diagnostic-not-object-safe/20973/10 "2024-06-12T03:38:11Z")

</div>

Using established terms for something which is kinda-sorta similar in Rust, but not really the same thing, is the source of a lot of confusion to people learning the language. Lifetimes, references, objects, downcasting, ...

If the Rust concepts were completely new, it might actually be less confusing than something close enough to throw people off. [I wrote this section](https://quinedot.github.io/rust-learning/dyn-trait-overview.html#dyn-trait-is-not-a-supertype) because of all the times I've seen people get confused by what `dyn Trait` is or means (typically due to an OO background when it comes to "object" confusion).

I prefer "`dyn`-safe" as there's an immediate connection to `dyn Trait`. If `dyn Trait` didn't use to be spelled just `Trait`, the terminology probably would have been different from the start. As it is, I already try to not lead with "trait object" on URLO; people generally recognize `dyn Trait` but haven't necessarily heard "trait object" yet.

(I do still use "trait objects" somewhat frequently because there's not always another suitable noun that comes to mind. And probably also out of inertia.)

---

<div class="post-metadata">

**Author:** ![kpreid](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kpreid/32/8484_2.png) [@kpreid](https://internals.rust-lang.org/u/kpreid)\
**Post date:** [June 12, 2024, 1:32pm UTC](https://internals.rust-lang.org/t/add-diagnostic-not-object-safe/20973/11 "2024-06-12T13:32:33Z")

</div>

> [@dhm](#):
>
> Nowadays the preferred term is `dyn`-safety

Someone should tell rustdoc (now putting "this trait is not object safe" on every applicable trait page) and [the reference](https://doc.rust-lang.org/reference/items/traits.html#object-safety), then.

The term possibly _should_ be replaced, but it certainly has not _been_ replaced. And inconsistent terminology is more confusing to newcomers than weird terminology.

---

<div class="post-metadata">

**Author:** ![binarycat](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/binarycat/32/12210_2.png) [@binarycat](https://internals.rust-lang.org/u/binarycat)\
**Post date:** [June 13, 2024, 1:23am UTC](https://internals.rust-lang.org/t/add-diagnostic-not-object-safe/20973/12 "2024-06-13T01:23:05Z")

</div>

> [@quinedot](#):
>
> typically due to an OO background when it comes to "object" confusion

i guess it's probably just confusing to high-level OO programmers then, since i've had a decent amount of experience with it, and i found it fairly clear.

---

<div class="post-metadata">

**Author:** ![simonbuchan](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/simonbuchan/32/9390_2.png) [@simonbuchan](https://internals.rust-lang.org/u/simonbuchan)\
**Post date:** [June 13, 2024, 1:41am UTC](https://internals.rust-lang.org/t/add-diagnostic-not-object-safe/20973/13 "2024-06-13T01:41:25Z")

</div>

So.. "interfacey traits" and "templatey traits" is right out then? 😋

---

<div class="post-metadata">

**Author:** ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)\
**Post date:** [June 13, 2024, 12:09pm UTC](https://internals.rust-lang.org/t/add-diagnostic-not-object-safe/20973/14 "2024-06-13T12:09:35Z")

</div>

I like the feature, but the term Object Safe is an awful jargon.

I've opened a separate thread to bikeshed about the name:

> [@Object Safety is a terrible term](https://internals.rust-lang.org/t/object-safety-is-a-terrible-term/21025):
>
> The term "Object Safety" is like a "Guinea Pig" — not a pig, and not from Guinea. Rust isn't a typical OOP language, so what is an "Object" (in the broad meaning) in Rust is unclear, and could be multiple things depending on one's interpretation. However, Object Safety is not about any objects, it's about traits. It's an abbreviated version of "Trait-Object Safety", but it leaves out the most relevant word! It uses a colloquial meaning of safety unrelated to Rust's primary concept of safe/un…

---

<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:** [September 11, 2024, 12:09pm UTC](https://internals.rust-lang.org/t/add-diagnostic-not-object-safe/20973/15 "2024-09-11T12:09:58Z")

</div>

Closed on September 11, 2024, 12:09pm UTC

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