Object Safety is a terrible term

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!

Object Safety uses a colloquial meaning of safety unrelated to Rust's primary concept of safe/unsafe. That's a very unfortunate choice given how important the other kind of safety is to Rust. Users who are not already familiar with the jargon have no chance of correctly deducing what "object safety" is about. The term is not just opaque, but has a false similarity to wrong things.

Fixes

At minimum the term could be clarified to be "Trait-Object Safety". This would make it clearly related to traits and trait objects, and still "backwards compatible" with the current wording.

But the "safety" here is really about compatibility, so it would be even better to call it "Trait-Object Compatible" or even "dyn-Trait Compatible" to make it entirely self-descriptive.

54 Likes

Dyn-trait compatible (or just dyn compatible for short) sounds good to me.

The main issue I see is that older learning resources will become out of date and this rename will cause confusion for new users. Can we have official docs (and error messages) include something like "... (previously known as object safe) ..."? That would alleviate that issue. Over a span of many years those clarifications could be phased out when the new term is well enough established.

11 Likes

The way I've always understood the object safety rules was that they are based on whether the compiler can construct a vtable for a trait or not. So perhaps the term "vtable compatible" should be considered. The advantage it would have over something like "dyn-trait compatible" is that it directly points to how the rules are derived... though "dyn-trait compatible" has a much stronger attachment to the direct language-level impact of the rules.

4 Likes

I would shorten it further: "dyn-compatible". Since that's the only thing the keyword dyn means, and then there's less words to stumble over in the phrase "this trait isn't dyn-trait compatible".

23 Likes

I think the name "trait object" is also confusing (to a lesser extent). In my mind it conveys an idea of some unique object that corresponds to a trait, which is obviously not what it means.

How about a "dynamic object" (formerly "trait object") that has a "dynamic type" (dyn T) corresponding to a "dynamic trait" (formerly "object-safe trait")? Not sure if "dynamic" is the perfect adjective for this, but if we're stuck with the dyn keyword I'd go with that.

2 Likes

"Dynamic object" and "dynamic type" make sense to me, but it should still be "dynamic-compatible trait" rather than "dynamic trait" because the trait doesn't have an opinion about whether it is dynamic or not, just about whether you "can use it that way if you want to"

1 Like

The trait is "dynamic" in the sense that it has the ability to form dynamic types and dynamic objects.

Fwiw - when I think of dynamic trait objects, I usually think "dyn trait" because that corresponds to the syntax for it. If somebody sees dyn <trait name> in code for the first time, they could plausibly google "dyn trait".

I don't really think it needs to be any more complicated than that. The names I would suggest are:

  • dyn-trait object
  • dyn-trait compatible (or "compatible with dyn-trait")

Any of these could be reasonably expanded to "dynamic", but I do think "dynamic trait" to describe a trait leaves too much open to the imagination. If I saw "this trait is dynamic" for the first time, I might wonder if it checks some impl requirements at runtime or something like that.

The error messages seem clearer too - "this trait is not compatible with dyn-trait because..." vs "this trait is not dynamic because..."

4 Likes

My experience when learning was that any reference to "dynamic typing" led to confusion. Things didn't clear up until I read some articles that drove home the fact that dyn Trait is a concrete, statically known type (I believe this one was instrumental).

3 Likes

However, there are two types involved. From the reference:

A trait object is an opaque value of another type that implements a set of traits.

There is dyn Trait, and then there is this "another type". This other type is what changes, i.e. is dynamic.

Like I said, I suggested the adjective "dynamic" because of the dyn keyword. The C++ keyword is "virtual". Another adjective would be "polymorphic".

I'm curious as to your background when you were learning rust. For me with a background in low/system level C++ it was pretty obvious.

The only surprise was the idea of fat pointers as opposed to embedding the vtable pointer into the struct (I seem to remember thinking "that's strange, seems inefficient for passing pointers" or something like that). After understanding traits a bit better it became obvious that it is the only possible way for Rust to be able to figure out the layout, given how much more flexible traits are than inheritance.

But maybe if you have a background in Python or such your experience will be different.

1 Like

I agree: "object-safe" is not a good description, and dyn Trait-compatible or just dyn-compatible is a much better term that would help people understand it more easily. This has come up a few times.

Potential concrete next steps: make a small lang proposal to use "dyn-compatible" rather than "object-safe" everywhere, and assuming we get consensus to do so, send PRs to change user-visible instances of "object-safe" (e.g. in error messages, documentation, and the reference).

17 Likes

So, in-context I understand what you're saying, and in-context, it makes perfect sense. But out-of-context, "dynamic type" sounds like an oxymoron, like "broke billionaire", or "subterranean skyscraper". The reason for that is that type systems, as objects of study in literature anyway, are by definition static i.e. part of the compilation process¹.

While I can see why you're advocating for the word "dynamic" (we're "stuck" with dyn, not that there's a clear better alternative), there's a possibility that using the word for this purpose might just introduce new questions. That could possibly be dealt with by introducing the term as a homonym, explicitly giving it an in-situ new meaning in documentation². Whether that's wise or not, I cannot say.

I do agree that "dyn-compatible" sounds clearer than the rather nebulous term "object-safe".

¹ The systems used at the core of the likes of Python, JS and Ruby are more akin to dynamic tagging, which allows for distinguishing between different groups of values (e.g. int vs list) but doesn't allow for preventing errors before the code runs. Though Python and JS have apparently seen the utility in types, growing support for some kind of optional / gradual type system and TypeScript, respectively.

² What's in a name, anyway?

1 Like

I'm hardly an expert in academic type systems, but in my limited experience, they don't care at all about the practical language distinction between static and dynamic types, in that they don't even consider compilation to exist in their model.

They (again, in my experience) mostly consider things like if types are substitutable in a logical sense (eg variance or unification, etc), or determining if an expression type checks (including type inference).

2 Likes

6 posts were merged into an existing topic: Different notions of "Safety" in Rust terminology

+1 for "dyn-compatible (trait)". It is pretty concise (important for making it popular), references the keyword for which the property is relevant, and does not contain potentially confusing use of "safety".

10 Likes

I've ignored this distinction not because I don't know there are all these kinds of safety, but because these details don't matter to the point I'm making.

I want to point out that this Rust term can be easily misunderstood by users. I'm using a descriptivist perspective here — the RFCs and dictionary definitions can be perfectly technically correct, but still fail to communicate the right meaning to users. In Rust's context "not safe" usually means something dangerous, incorrect, or lacking safety checks. Not object-safe is not that. It's properly checked, and it's just an API design choice, not a defect. The term is not even helpful in the context of type safety. Usually when something is described as "not type safe" it means lacking sufficient type checking at compile time, and potentially leading to runtime type errors. Not object-safe is not that either.

13 Likes

the main problem i see is that, while there are plenty of great options to replace the term "object safe", i don't see a good replacement term for "trait object". it seems to me that their implementation using vtables does indeed make them most similar to C++ objects (only really lacking inheritance and downcasting, at least one of which should be addressed in the near future).

the best option i can see right now would be "trait object compatible", which doesn't solve all of the problems, but would be a definite improvement.

2 Likes

"Object" is a very loaded word. Arguably, we should avoid it to prevent potential confusion related to previous OOP knowledge. Also, "trait object" usually used for types like Box<dyn Trait> and not for reference types (e.g. &dyn Trait).

In the vein of "dyn compatible", we could rename "trait objects" to "dyn types", which would naturally apply to both Box<dyn Trait> and &dyn Trait types.

I've looked at changing this, and unsurprisingly there are a lot of uses of "object safety", and even more uses of "trait object", all over rustc and documentation.

A couple of questions:

  1. I think replacing "object safe" with "trait-object safe" is an easy improvement to make, but in the particular case of "object-safe trait", the "trait-object-safe trait" sounds repetitive. Should I leave it as "object-safe" when it's directly referring to a trait?
    Same goes for repetitiveness of "dyn Trait-compatible trait".

  2. There's a very common error text, included in hundreds of ui tests. Should it stay like that?

    the trait $Trait cannot be made into an object

3 Likes