Reputation:
One task of my university homework is to sort data in a structure with variant fields (a union). We usually use C, but I decided to try Rust and faced a problem.
One of the structs inside the union must include a string. As far as I understand this string is the problem.
My structure:
pub struct Show {
type_name: ShowType,
type_data: ShowTypeData,
}
enum ShowType {
Childish,
Adultish,
}
union ShowTypeData {
childish: ChildishShow,
adultish: AdultishShow,
}
struct ChildishShow {
age_target: i8,
}
struct AdultishShow {
subtype: String,
}
Errors:
error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
--> src/lib.rs:10:5
|
10 | childish: ChildishShow,
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
help: wrap the field type in `ManuallyDrop<...>`
|
10 | childish: std::mem::ManuallyDrop<ChildishShow>,
| +++++++++++++++++++++++ +
I don't understand what "Copy" and "Drop" mean exactly.
Upvotes: 3
Views: 3188
Reputation: 10514
Rust, like C++, is a language that tries to bring together high level features and low level control. Unlike C++ it also tries extremely hard to be "safe by default".
Copy
indicates a type that is trivially copyable—that is, it is safe for the compiler to make a bitwise copy of it. Drop
indicates a type where the compiler will execute clean-up code when a valid value of the type goes out of scope (similar to a destructor in C++).
Copy
and Drop
are mutually exclusive. A type that implements "Copy" is forbidden from implementing "Drop" and vice-versa.
Plain unions interact badly with RAII and safety. If you don't know what type is stored somewhere then you can't safely retrieve it. Furthermore the compiler can't clean it up.
Rust has unions because they are useful for FFI and in a few other corner cases, but idiomatic Rust rarely uses them.
You could make your code build by wrapping the fields in ManuallyDrop
. This is a special wrapper that can be placed around any type to prevent it from being dropped automatically. Fields of type ManuallyDrop
are explicitly allowed inside unions*.
But that is not what you should do. Instead, you should use the features of Rust enums to allow the compiler to manage the relationship between the tag and the content. That way the compiler can make sure you retrieve the correct type, and can clean the value up when it goes out of scope.
* In Rust a number of standard library types have "special privileges" in the language.
Upvotes: 1
Reputation: 98486
Do not copy unions from C to Rust verbatim.
Write instead:
pub struct Show
{
theatre: String,
name: String,
director: String,
price_min: i64,
price_max: i64,
show_type: ShowType,
}
enum ShowType
{
Childish(ChildishShow),
Adultish(AdultishShow),
Musical(MusicalShow),
}
Unions are a low-level, intrinsically unsafe construct in Rust, and quite limited about what you can put in. Use an enum
instead when possible. No unions, no problems.
Upvotes: 8