# Blog post: Extended Enums and Thin Traits

**URL:** <https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755>\
**Category:** language design\
**Created:** [October 8, 2015, 4:51pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755 "2015-10-08T16:51:45Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![nikomatsakis](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nikomatsakis/32/5410_2.png) [@nikomatsakis](https://internals.rust-lang.org/u/nikomatsakis)\
**Post date:** [October 8, 2015, 4:51pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/1 "2015-10-08T16:51:45Z")

</div>

I wrote a blog post that compares the “Extended Enums” stuff I was describing with the “Thin Traits” (my term) stuff that @aturon was describing:

[http://smallcultfollowing.com/babysteps/blog/2015/10/08/virtual-structs-part-4-extended-enums-and-thin-traits/](http://smallcultfollowing.com/babysteps/blog/2015/10/08/virtual-structs-part-4-extended-enums-and-thin-traits/)

---

<div class="post-metadata">

**Author:** ![Gankra](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/gankra/32/6002_2.png) [@Gankra](https://internals.rust-lang.org/u/Gankra)\
**Post date:** [October 8, 2015, 7:22pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/2 "2015-10-08T19:22:33Z")

</div>

I’m confused, I thought Servo wanted closed inheritance? Have the devs indicated otherwise?

---

<div class="post-metadata">

**Author:** ![nikomatsakis](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nikomatsakis/32/5410_2.png) [@nikomatsakis](https://internals.rust-lang.org/u/nikomatsakis)\
**Post date:** [October 8, 2015, 7:24pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/3 "2015-10-08T19:24:44Z")

</div>

> [@Gankra](#):
>
> I'm confused, I thought Servo wanted closed inheritance? Have the devs indicated otherwise?

I think they can work with either. Certainly @aturon found that the uses of downcasting were relatively few. Perhaps @pcwalton or @jdm can chime in, though.

---

<div class="post-metadata">

**Author:** ![aturon](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aturon/32/3272_2.png) [@aturon](https://internals.rust-lang.org/u/aturon)\
**Post date:** [October 8, 2015, 8:01pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/4 "2015-10-08T20:01:50Z")

</div>

> [@nikomatsakis](#):
>
> Certainly @aturon found that the uses of downcasting were relatively few.

That's not quite right -- the use of _virtual dispatch_ was smaller than I expected. They definitely use downcasting, but given their setup they could easily generate the needed methods.

From my point of view, this isn't so much about prioritizing open vs closed as the other risks around the closed proposal that you didn't mention much in the post -- specifically, the impact to inference/subtyping. Going with the thin traits proposal is definitely sufficient for Servo (and many other cases), and is almost certainly faster and less risky on our end.

---

<div class="post-metadata">

**Author:** ![erickt](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/erickt/32/4498_2.png) [@erickt](https://internals.rust-lang.org/u/erickt)\
**Post date:** [October 8, 2015, 9:26pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/5 "2015-10-08T21:26:59Z")

</div>

This potentially is a very silly question, but with respect to the thin trait design, how would this interact with classic fat pointers, as in:

```rust
struct NodeFields {
    id: u32
}

trait Node: NodeFields {
    fn something(&self);
    fn something_else(&self);
}

fn foo(node: &Node) {
    println!("{} {}", node.id, node.something());
}

```

Would this be an error, or would the `node.id` be just dispatched through the vtable?

---

<div class="post-metadata">

**Author:** ![nikomatsakis](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nikomatsakis/32/5410_2.png) [@nikomatsakis](https://internals.rust-lang.org/u/nikomatsakis)\
**Post date:** [October 8, 2015, 10:38pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/6 "2015-10-08T22:38:08Z")

</div>

> [@aturon](#):
>
> That's not quite right -- the use of virtual dispatch was smaller than I expected. They definitely use downcasting, but given their setup they could easily generate the needed methods.

OK, thanks for the correction.

> [@aturon](#):
>
> From my point of view, this isn't so much about prioritizing open vs closed as the other risks around the closed proposal that you didn't mention much in the post -- specifically, the impact to inference/subtyping.

Yes, I didn't emphasize the "risks" aspect in the post, but I agree with this. I actually had a paragraph about it but cut it, since I didn't seem to be adding anything to what I had said before. 🙂

---

<div class="post-metadata">

**Author:** ![nikomatsakis](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nikomatsakis/32/5410_2.png) [@nikomatsakis](https://internals.rust-lang.org/u/nikomatsakis)\
**Post date:** [October 8, 2015, 10:38pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/7 "2015-10-08T22:38:37Z")

</div>

> [@erickt](#):
>
> Would this be an error, or would the node.id be just dispatched through the vtable?

Neither, I think. The field would just be loaded from the data pointer at offset zero.

---

<div class="post-metadata">

**Author:** ![stebalien](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/stebalien/32/228_2.png) [@stebalien](https://internals.rust-lang.org/u/stebalien)\
**Post date:** [October 9, 2015, 12:56pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/8 "2015-10-09T12:56:11Z")

</div>

_edit:_ incorrect. See [below](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/14?u=stebalien).

~~`#[repr(thin)]` is going to be a performance/backcompat footgun (easy to leave off) and is going to confuse people. Also, repr should only affect representation but this one also affects semantics (can’t implement trait from other crates). What about:~~

```rust
closed trait Test {
    // ....
}

```

where all closed traits are implicitly optimized.

---

<div class="post-metadata">

**Author:** ![lucian1900](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/lucian1900/32/399_2.png) [@lucian1900](https://internals.rust-lang.org/u/lucian1900)\
**Post date:** [October 9, 2015, 1:53pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/9 "2015-10-09T13:53:15Z")

</div>

I agree. I was thinking as I was reading the blog post that `local` or `closed` might make sense as a name for these traits.

---

<div class="post-metadata">

**Author:** ![nikomatsakis](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nikomatsakis/32/5410_2.png) [@nikomatsakis](https://internals.rust-lang.org/u/nikomatsakis)\
**Post date:** [October 9, 2015, 1:56pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/10 "2015-10-09T13:56:35Z")

</div>

> [@stebalien](#):
>
> Also, repr should only affect representation but this one also affects semantics (can't implement trait from other crates).

I feel like it is legitimate for representation choices to cause some operations to become unavailable. What would be bad is if the same operation has two very distinct meanings, depending on the representation. It seems like having a thin representation prohibit other crates from implementing the trait falls into the former category; this seems analogous to how using a packed representation will make taking references to fields unsafe. Another example might be allowing enums (at some future point) to use low-order bits to distinguish variants, thus achieving tagged pointers: this would have to forbid taking references into those enums.

**EDIT:** As @aturon later pointed out, thin traits can be implemented by other crates -- that's what makes the "open extensibility" I talked about in my post -- but they cannot be implemented for types of other crates. Anyway, the broader point stands.

---

<div class="post-metadata">

**Author:** ![petrochenkov](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/petrochenkov/32/2784_2.png) [@petrochenkov](https://internals.rust-lang.org/u/petrochenkov)\
**Post date:** [October 9, 2015, 3:33pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/11 "2015-10-09T15:33:23Z")

</div>

If `#[repr(thin)]` prevents implementation of a trait from other crates, isn’t it the same thing as sealed traits ([https://github.com/rust-lang/rfcs/search?q=sealed](https://github.com/rust-lang/rfcs/search?q=sealed))? I.e. it will allow to lift some restrictions from coherence as well.

---

<div class="post-metadata">

**Author:** ![Gankra](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/gankra/32/6002_2.png) [@Gankra](https://internals.rust-lang.org/u/Gankra)\
**Post date:** [October 9, 2015, 3:43pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/12 "2015-10-09T15:43:31Z")

</div>

`repr(packed)` already has some strong semantic impact (internal references are unsafe). Arguably `repr(C)` and the rest also have a semantic impact if alignment and size are important aspects to a program’s correctness (e.g. binding to hardware interfaces).

---

<div class="post-metadata">

**Author:** ![stebalien](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/stebalien/32/228_2.png) [@stebalien](https://internals.rust-lang.org/u/stebalien)\
**Post date:** [October 9, 2015, 3:44pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/13 "2015-10-09T15:44:49Z")

</div>

I don’t like the situation with packed structs either but at least that case is _very_ rare. IMO, no one should be exporting packed structs across crates except for, maybe, c bindings. I don’t think packed enums should use repr either but I’ll argue that case when it comes up.

My main problem with `#[repr(thin)]` is that it will significantly affect public APIs. Maybe we need an RFC outlining what attributes in general should and should not be used for (I see them as compiler directives but this is going well beyond compiler directives).

---

<div class="post-metadata">

**Author:** ![aturon](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aturon/32/3272_2.png) [@aturon](https://internals.rust-lang.org/u/aturon)\
**Post date:** [October 9, 2015, 3:47pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/14 "2015-10-09T15:47:08Z")

</div>

> [@petrochenkov](#):
>
> If #[repr(thin)] prevents implementation of a trait from other crates, isn't it the same thing as sealed traits ([Repository search results · GitHub](https://github.com/rust-lang/rfcs/search?q=sealed))? I.e. it will allow to lift some restrictions from coherence as well.

That's not the limitation being imposed here. Rather, you can only impl a thin trait for types that you define (where usually, if you define a new trait, you can apply it to existing types). That's because a thin trait influences the struct layout by inserting a vtable.

To be concrete, you can define a thin trait in crate A, and in crate B, you can impl that thin trait for a struct defined in crate B.

---

<div class="post-metadata">

**Author:** ![aturon](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aturon/32/3272_2.png) [@aturon](https://internals.rust-lang.org/u/aturon)\
**Post date:** [October 9, 2015, 3:50pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/15 "2015-10-09T15:50:24Z")

</div>

> [@stebalien](#):
>
> My main problem with #[repr(thin)] is that it will significantly affect public APIs. Maybe we need an RFC outlining what attributes in general should and should not be used for (I see them as compiler directives but this is going well beyond compiler directives).

I used to feel exactly the same way, and had long pushed for this to be a keyword when we were working through the design. But I've since come around to @nikomatsakis's position: the _behavior_ of traits does not vary at all, and the choice here is entirely about optimization of representation, that happens to also limit (but not change) the cases where the trait can be applied.

It's worth keeping in mind that, due to the orphan rules, the primary place where a trait is applied to types defined elsewhere is in the crate defining the trait -- since that's the only one that can do so arbitrarily. So I suspect this representation choice will mostly affect the trait definer rather than downstream crates in practice.

---

<div class="post-metadata">

**Author:** ![stebalien](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/stebalien/32/228_2.png) [@stebalien](https://internals.rust-lang.org/u/stebalien)\
**Post date:** [October 9, 2015, 4:01pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/16 "2015-10-09T16:01:40Z")

</div>

> [@Gankra](#):
>
> Arguably repr(C) and the rest also have a semantic impact if alignment and size are important aspects to a program's correctness (e.g. binding to hardware interfaces).

The difference is that `repr(C)` (versus `repr(packed)`) modifies compiler-level semantics. They don't affect rust as a high-level language, just how the compiled binary interacts with other programs and the hardware. In general, I'm fine having representation affect _unsafe_ operations.

> [@aturon](#):
>
> It's worth keeping in mind that, due to the orphan rules, the primary place where a trait is applied to types defined elsewhere is in the crate defining the trait -- since that's the only one that can do so arbitrarily. So I suspect this representation choice will mostly affect the trait definer rather than downstream crates in practice.

Good point. I withdraw my objection (to `repr(thin)`, I still object to `repr(packed)` but that ship has sailed).

Unless I'm mistaken, the compiler could theoretically apply `repr(thin)` automatically to all traits not implemented on foreign types. If that's the case, this really is just a compiler hint saying "compiler, don't allow me to do something that will disallow this optimization" and doesn't affect API.

---

<div class="post-metadata">

**Author:** ![td\_](https://avatars.discourse-cdn.com/v4/letter/t/d6d6ee/32.png) [@td\_](https://internals.rust-lang.org/u/td_)\
**Post date:** [October 10, 2015, 11:58pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/17 "2015-10-10T23:58:48Z")

</div>

`#[repr (thin)]` only affects the local crate’s ability to implement the trait because orphan rules already preclude other crates from implementing that trait for alien types, correct? Is there another reason it wouldn’t be back compat to _remove_ a thin repr tag from a trait? Is the performance characteristic of thin pointers always a win whenever you don’t need the flexibility of fat traits?

If the answer to these qs is yes, no, yes, wouldn’t it be optimal for the compiler to just use thin pointers for trait objects unless it can’t?

---

<div class="post-metadata">

**Author:** ![nikomatsakis](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nikomatsakis/32/5410_2.png) [@nikomatsakis](https://internals.rust-lang.org/u/nikomatsakis)\
**Post date:** [October 11, 2015, 10:54am UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/18 "2015-10-11T10:54:03Z")

</div>

Implementing a thin trait adds an implicit vtable at the head of the struct. This will affects its size, naturally, and could break unsafe code, as well as other assumptions. It might also affect performance of plain safe code, depending on the ratio of struct-to-object instances (i.e., if you don’t use objects, as is common for traits, you’re just wasting memory). I think it should definitely be something you opt into.

---

<div class="post-metadata">

**Author:** ![anon32976453](https://avatars.discourse-cdn.com/v4/letter/a/49beb7/32.png) [@anon32976453](https://internals.rust-lang.org/u/anon32976453)\
**Post date:** [October 11, 2015, 7:27pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/19 "2015-10-11T19:27:33Z")

</div>

this doesn't make sense to me. IIRC, there are two possible use cases here:

- Either:

> ```
> struct S{...}
> #[repr(thin)]
> trait T : S {...}
> 
> ```

- Or:

> #[repr(thin)]  
> trait T {...}

In the former case the same crate provides both the struct and the trait so the coherence doesn't matter and in the latter the struct could be defined as in your example in a client crate but then the upstream crate forces an unnecessary layout decision on its clients without knowing the client struct's size and it could actually make performance worse (e.g when the struct fits a cache line exactly and adding a vtable inline will cause it to be bigger than a single cache line. This decision is better made at the same location (by the same person) defining the data layout, as part of the struct definition in the client crate and not on the trait.

As I said on the reddit thread, i really like all the separate pieces of these suggestions but the way those pieces are put together is wrong IMO. it is backwards and breaks Rust's current very clear and orthogonal design and violates the separation of concerns principle.  
traits in rust define interfaces and concrete types (enums and structs..) define data layout. clearly we want to affect the data layout and therefore this does not belong in the trait definition.

---

<div class="post-metadata">

**Author:** ![nical](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nical/32/4612_2.png) [@nical](https://internals.rust-lang.org/u/nical)\
**Post date:** [October 13, 2015, 12:51pm UTC](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755/20 "2015-10-13T12:51:44Z")

</div>

This is odd that #[repr(thin)] which is a property of the struct (the fact that it has a vtable in it) is marked on the trait (which in my mind is where you declare the interface that the data implement but don’t actually impose any data (except with the hypothetical struct inheritance stuff but which is more like adding an explicit contract that some data is present, than the implicit vtable pointer here). I assume that marking the trait thin rather than the struct is because of the &-syntax (and transparency of fat pointers in general) where &Foo could be a fat or thin pointer depending on whether the vtable is in the pointer or the structure. I get it that we don’t want to add a specific syntax when talking about the pointer itself but on the other hand it is odd because this is actually where it matters (on the pointer) so it would make sense to be explicit there instead of on the definition of the interface. I can’t think of a convenient way to express it A thin pointer to something that implements Foo would look like

```
&thin Foo

```

and a fat pointer would remain

```
&Foo

```

The declaration of the interface and the way the virtual dispatch is made could be orthogonal and I think it should in an ideal world. Perhaps it is too late for something backward-compatible.

[Next page](https://internals.rust-lang.org/t/blog-post-extended-enums-and-thin-traits/2755.md?page=2)
