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
}
