Error when using trait associated type as union field

I'm running into a strange error trying to use a trait's associated type as the type of a union field:

pub trait ReprTrait {
    type Repr;
}

pub struct MyStruct;

impl ReprTrait for MyStruct {
    type Repr = isize;
}

pub union MyUnion {
    // No error for using `isize` directly.
    foo: isize,
    
    // Using `ReprTrait::Repr` generates an error about
    // fields that need dropping.
    bar: <MyStruct as ReprTrait>::Repr,
}

Gives the error:

error[E0740]: unions may not contain fields that need dropping
  --> src/lib.rs:17:5
   |
17 |     bar: <MyStruct as ReprTrait>::Repr,
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
note: `std::mem::ManuallyDrop` can be used to wrap the type
  --> src/lib.rs:17:5
   |
17 |     bar: <MyStruct as ReprTrait>::Repr,
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Playground link.

I've tried to see if this has been reported as a bug anywhere but I haven't been able to find anything. Does anyone know what I'm missing here? Is there some restriction that's preventing the compiler from being able to tell that the associated type doesn't need to be dropped?

I believe the compiler is trying to protect you in the case that MyStruct happens to live in another crate and is changed by someone else. You don't want downstream code to be able to break upstream code; it only happens the other way around and that's when backwards compatability is broken.

If I make MyStruct::Repr = String, then I end up with a broken union, since there would be a requirement to drop the String.

Unions are only stable when all fields are Copy, if you need more than that you can wrap all non-Copy fields in ManuallyDrop and use the untagged_unions nightly feature.

This is because the drop guarantees for unions haven't been worked out just yet. i.e. should my_union.bar = foo; drop the old value? There are more involved cases that you can find by searching for untagged_unions, either in the tracking issue or the RFC

Huh, yeah changing the definition of ReprTrait to

pub trait ReprTrait {
    type Repr: Copy;
}

Fixes the error. So looks like you're right about the Copy bound (or lack thereof) being the source of the error.

What I don't really get, though, is why in this case the explicit Copy bound is necessary. If I were using ReprTrait::Repr in a generic context I would get not being able to assume that Repr: Copy. But in this case, I'm directly accessing the associated type on a concrete type. In other contexts I'm able to use an associated type on a concrete type as if it were a resolved, known type. For example, the following compiles fine:

fn requires_copy<T: Copy>(value: T) {}

fn main() {
    let val: <MyStruct as ReprTrait>::Repr = 0;
    requires_copy(val);
}

This example seems to do the same thing the union is complaining about (assuming that MyStruct::Repr is Copy). What's the difference between these two cases that makes requires_copy work but not using MyStruct::Repr in a union?

Playground link.

Huh, the first time I didn't look too closely at your code so I didn't notice that it was all concrete types. This does look like a bug to me, I don't know if this has already been noticed, I think that it should be fine to file an issue for this.

1 Like

Ah, in going to write up a bug report I also tested the example case against the beta and nightly compilers and it looks like the issue is fixed in nightly :tada: Glad to see that this should be fixed in a couple of versions.

2 Likes

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