I'm new to Rust and trying to understand how reference works. Below is a simple function.
fn f1(x: &i32) -> &i32{
x
}
Since x
is of type &i32
, return it directly matches the return type &i32
. But I found that if I change the function to this, it also compiles without any problem:
fn f1(x: &i32) -> &i32{
&x
}
Here x
is of type &i32
, &x
is of type &&i32
and doesn't match the return type &i32
. Why it compiles?
CodePudding user response:
This is a result of type coercion.
To make the language more ergonomic, a certain set of coercions are allowed in specific situations. One of the situations is determining the return value of a function.
Among the allowed coercions is this:
&T or &mut T to &U if T implements Deref<Target = U>
In this particular case, there is an implementation of the deref trait in the std library:
impl<T: ?Sized> const Deref for &T {
type Target = T;
#[rustc_diagnostic_item = "noop_method_deref"]
fn deref(&self) -> &T {
*self
}
}
In your case, the target T
is i32
, and the trait is implemented for &T
(ie. &&i32
) so the value can be coerced from &&i32
to &i32
.
CodePudding user response:
In your second function Rust automatically dereferences &x
to x
(i.e. &&i32
to &i32
). That's done via the so called "Deref coercion".
Consider this example:
fn f1(x: &i32) -> &i32{
let t1: &&i32 = &x; // nothing special here
let t2: &i32 = &x; // this works too
let t3: &i32 = &&&&x; // this works too!
let t4: i32 = x; // this doesn't work though
x
}