First: in Rust, there are no implicit costly copies, unlike in, for example, C++. Whereas in C++, the default action is "deep copy" (via copy constructor or similar), the default action in Rust is moving. A move is a shallow copy which (a) is usually very small and cheap and (b) can be removed by the optimizer in most cases. To get deep clones in Rust you have manually use .clone()
. If you don't do that, you usually don't really have to worry about this.
Second: matching on an enum only looks at the discriminant of that enum (unless you bind enum fields, see below). That's the "tag" or the "metadata" which specifies which variant of the enum is stored in a value. That tag is tiny: it fits in 8 bits in almost all cases (enums with more than 256 variants are rare). So you don't need to worry about that. And in your case, we have a C-like enum without any fields. So the enum only stores the tag and hence is tiny, too.
So what about enum fields that might be costly to copy? Like this:
enum SomeEnum {
SomeEntry(String),
AnotherEntry,
}
let boxed_value = Box::new(SomeEnum::AnotherEntry);
match *boxed_value {
SomeEnum::SomeEntry(s) => drop::<String>(s), // make sure we own the string
SomeEnum::AnotherEntry => {},
}
So in this case one variant stores a String
. Since deep-copying a string is somewhat costly, Rust won't do it implicitly. In the match arm we try to drop s
and assert it's a String
. That means we (meaning: the body of the match arm) own the string. So, if the match arm owns it but we didn't get the owned value from cloning it, that means that the outer function doesn't own it anymore. And in fact, if you try to use boxed_value
after the match, you will get move errors from the compiler. So again, either you get a compiler error or no bad things automatically happen.
Furthermore, you can write SomeEnum::SomeEntry(ref s)
in the match
. In that case, the string is bound by reference to s
(so the drop()
call won't work anymore). In that case, we never move from boxed_value
. This is something I call "deferred moving", but I'm not sure if that's an official term for it. But it just means: when pattern matching, the input value is not moved at all until a binding in the pattern moves from it.
Lastly, please take a look at this code and the generated assembly. The assembly is optimal. So once again: while you might be worried about accidental clones when you come from the C++ world, this is not really something you need to worry about in Rust.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…