Reputation: 3461
Rust allows assigning references with a higher level of indirection to references with a lower level of indirection. For instance, the compiler allows assigning a &&&&&&
to a &
:
fn main() {
let mut some_number = 5;
// assign an &&&&&&i32 to an &i32, which works.
let reference : &i32 = &&&&&&some_number;
}
This also works for function parameters:
fn main() {
let num = 5;
// ref1 is an &&i32
let ref1 = &#
// Pass an &&i32 to a function parameter, which itself is an &i32 (works)
func(ref1);
}
fn func(test: &i32) {
println!("^^^^ This works!");
}
I've learned that this works because of automatic dereferencing, which allows the Rust compiler to dereference a type as much as it needs to match some other type (please correct me if I'm wrong on this).
However, Rust doesn't seem to allow assigning lower-indirection references to higher-indirection references:
fn main() {
let num = 5;
// Try assigning an &i32 to an &&i32 (error)
let ref1 : &&i32 = #
}
This results in an expected &i32, found integer
compiler error. We get a similar compiler error when testing this with function parameters:
fn main() {
let num = 5;
// ref1 is an &&&i32
let ref1 = &&#
// Try passing an &&&i32 to a function parameter of type &&&&&i32 (error)
func(ref1);
}
fn func(test: &&&&&i32) {
println!("^^^^^^^^ This does not work!")
}
Here, we get a mismatched types
error as well. Something I'm curious about, however, is that the compiler output isn't exactly what we expect. Rather than expected &&&&&i32, found &&&i32
, the compiler error is expected &&i32, found integer
. It seems that the compiler dereferenced both references until one was no longer a reference - why does it dereference both references? I thought it only dereferenced whatever was being passed to the function.
Overall, my main question is Why, exactly, should assigning lower-indirection to higher-indirection references be disallowed when assigning higher-indirection to lower-indirection references is allowed? What is so different about these two things, that their behaviors must be different as well?
Upvotes: 1
Views: 116
Reputation: 27357
&&T
can be coerced to &T
because of deref coercion ("&T
or &mut T
to &U
if T
implements Deref<Target = U>
") and the impl Deref<Target = T> for &T
the other way is not possible because there exists no impl Deref<Target = &T> for T
.
By repeatet application &&&&&&T
can be coerced to &T
As to why one is allowed while the other isn't well if implicit referencing was allowed everywhere tracking ownership would be even harder than it currently is, we have this problem already with auto-referencing of method receivers.
let s = String::from("Hello");
my_fun(s);
The question "Does s
get moved?" can't be answered without looking at the definition of my_fun
if we allow automatic referencing.
Upvotes: 1