Reputation: 2368
Given a struct with a generic Option<T>
where T
might not implement Clone
why can't None
be cloned? Isn't a None
of type T
the same as any other None
? For example:
struct Foo<T> {
bar: Vec<Option<T>>,
}
impl <T> Foo<T> {
fn blank(size: usize) -> Foo<T> {
Foo {
bar: vec![None; size],
}
}
}
Upvotes: 11
Views: 2167
Reputation: 70900
If the number of items is not too large, so that it overflows the stack if placed on it, you can construct the Vec
from an array. The advantage of arrays is that they can be constructed from const
values, even if the type is not Copy
:
const NONE: Option<NonClone> = None;
Vec::from([NONE; 10])
Upvotes: 0
Reputation: 154876
Isn't a
None
of typeT
the same as any otherNone
?
Definitely not! Unlike reference-based languages where null is typically internally represented as a null pointer (pointer whose address is 0), Rust's Option<T>
introduces no indirection and stores T
inline when the option is Some
. Since all enum variants have the type of the enum and therefore the same size, the None
variant must still occupy at least as much space as T
.
Having said that, it is technically true that the None
value could be cloned without T
being Clone
simply because the None
variant of the enum doesn't contain the T
, it only stores the discriminator and reserves space that could contain T
if the variant were to change to Some
. But since Rust enum variants are not separate types, a trait bound defined for the enum must cover all variants.
See other answers more detailed explanations and instructions how to create a vector of None
values of a non-cloneable Option
.
Upvotes: 8
Reputation: 13518
An Option
can only be cloned if the inner T
implements Clone
:
impl<T> Clone for Option<T>
where
T: Clone,
Isn't a
None
of typeT
the same as any otherNone
?
Actually, no. The Rust compiler does not even view None
as a type. Instead, None
is just a variant (subtype) of Option<T>
. Try comparing None
s of two different T
s:
let a: Option<String> = None;
let b: Option<u8> = None;
assert_eq!(a, b)
You will see that they are in fact completely unrelated types:
error[E0308]: mismatched types
--> src/main.rs:4:5
|
4 | assert_eq!(a, b)
| ^^^^^^^^^^^^^^^^ expected struct `String`, found `u8`
|
= note: expected enum `Option<String>`
found enum `Option<u8>`
So the Rust compiler actually sees None
as Option::<T>::None
. This means that if T
is not Clone
, then Option<T>
is not Clone
, and therefore Option::<T>::None
cannot be Clone
.
To make your code compile, you must constrain T
to Clone
:
struct Foo<T: Clone> {
bar: Vec<Option<T>>,
}
impl <T: Clone> Foo<T> {
fn blank(size: usize) -> Foo<T> {
Foo {
bar: vec![None; size],
}
}
}
Now the compiler knows that T
is Clone
, and the implementation of Clone
for Option<T>
(and Option::<T>::None
) is fulfilled.
Upvotes: 2
Reputation: 16475
As the other answers correctly point ouf, this is due to the way the vec!
-macro is implemented. You can manually create a Vec
of any Option<T>
without requiring T
to be Clone
:
let bar = std::iter::repeat_with(|| Option::<T>::None).take(size).collect::<Vec<_>>();
This will create size
-number of Option::<T>::None
and place them in a Vec
, which will be pre-allocated to the appropriate size. This works for any T
.
Upvotes: 6
Reputation: 35512
If one part can't be cloned, then no parts can be cloned. Think about this from a type perspective: you have a variable called foo
of type Option<T>
, where T
isn't clonable. Sure, None
can be cloned, but Some(_)
can't, and the type of the variable doesn't tell you which it is. So, from a compile-time perspective, you can't clone the option.
Upvotes: 3