I'm slowly starting Rust for Rustaceans, and it's already poking holes in my understanding of Rust. Here's a couple initial questions I have:
A shared reference, &T is , as the name implies, a pointer that may be shared. Any number of references may exist to the same value, and each shared reference is Copy, so you can trivially make more of them
I don't understand why a shared reference has to implement copy. In fact, isn't this not true just by the fact that references work for Strings and Strings size can't be known at compile time?
- I'm having trouble with the idea of assigning a new value to a mutable reference.
let mut x = Box::new(42); *x = 84;
Why in this example, is the assignment dereferenced. Why not just do x=84? is it dereferenced specifically because is Boxed on the heap?
A reference &T holds a pointer, ie. the memory adress to the actual content of T
So, in the example x doesn't hold the value 42, it holds the memory adress to the memory there the integer value 42 is stored. So, to access the value, you need to dereference the reference. Which is why you need to use *x when you assign the value.
And the Copy question.It is not that s reference has to implement Copy. A reference IS Copy, by the simple fact that it is a primitive value on the stack.
A reference IS Copy, by the simple fact that it is a primitive value on the stack.
This seems a bit misleading, noting that unique/mutable references aren't
Copy
. Shared references areCopy
because it's sound to have that, and it's a huge QOL improvement over the alternative.
In fact, isn’t this not true just by the fact that references work for Strings and Strings size can’t be known at compile time?
I don't understand this. Shared references to
String
areCopy
, too. This doesn't have to do anything with sizes. Rather, it's implemented in the compiler, because it's sound to have it and a huge QoL improvement over the alternative... just the same reason why e.g.usize
isCopy
, really.is it dereferenced specifically because is Boxed on the heap?
No, it's not really related to the heap.
Box
implementsDerefMut
, which is in-depth explained here.The key thing to understand is that in Rust, references are considered unique types. This means that
&T
is a separate type fromT
.So, for #1, it is not saying that
T
implementsCopy
, it is saying that regardless of whatT
is,&T
implementsCopy
. This is because, by definition, it is always valid to copy a shared reference, even ifT
itself is notCopy
.Part of the reason this is confusing is that traits often include references in their function signatures; and in particular,
Clone::clone
has the signaturefn clone(&self) -> Self
. So whenT
implementsclone
, it has a method that takes&T
and returnsT
. But even though the signature takes&T
, the type that implementsClone
isT
itself. (&T
always implementsClone
as well, just as it always implementsCopy
, but as withCopy
, this is independent from whetherT
itself implementsClone
. See for example the error message you get when explicitly cloning a shared reference: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=a1b80cc83570321868c4ad55ee3353dc)Since
Copy
is a marker trait, it doesn't have any associated methods making it explicit thatCopy
affects how&T
can be used. However,Copy
requires the type to implementClone
(even though you can implementClone
in terms ofCopy
) and implies thatT
can always be automatically created from&T
without an explicit call toT::clone
, so you can think of the "signature" forCopy
as matching that ofClone
, even though there's no actualcopy
method.For #2, I recommend thinking in terms of explicit types. Adding annotations, you get:
let mut x: Box = Box::new(42); *x = 84_i32;
The type of
x
isBox
. You cannot assign ani32
to aBox
; they're different types! But the type ofis
i32
, and thus you can assign84
to it.The trait used to make
Box
behave this way isDerefMut
, which explicitly makesassignable: https://doc.rust-lang.org/std/ops/trait.DerefMut.html