The println!
macro handles both values and references without requiring explicit dereferencing.
First, create a vector
let v = vec![0, 2, 3, -4];
Printing references from vec.iter
for x in v.iter() {
println!("x: {}", x);
}
Printing dereferenced elements from vec.iter
for x in v.iter() {
println!("x: {}", *x);
}
Printing values from vec
for x in v {
println!("x: {}", x);
}
How is the internal dereferencing in Case 1 done?
I know internally println!
makes another macro call but the last macro in the chain format_args!
is implemented at the compiler level and I have no view into it.
macro_rules! println {
($fmt:expr) => (print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}
macro_rules! print {
($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*)));
}
macro_rules! format_args {
($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
}
The important thing here is that using {}
in the format string invokes the Display
trait on the value passed.
As expected, the i32 type implements Display
, which is what allows your case #2 and case #3 to work. Because they are getting a standard i32
value, not a reference, it all works.
For your case #1, x
would be an &i32
, which seems to be the core of your question. The answer there is in the Display
trait. Display contains the following:
impl<'a, T> Display for &'a T
where
T: Display + ?Sized
which says "for the reference type of T
, implement Display
if T
implements Display
". This means that because i32
implements Display
, the reference type also implements it automatically.
There is no special type handling being done by the compiler here. The compiler-implemented code passes on that responsibility to the Display
trait's implementation.