macrosrustdereference

How Does Rust's println! macro perform dereferencing?


The println! macro handles both values and references without requiring explicit dereferencing.

First, create a vector

let v = vec![0, 2, 3, -4];
  1. Printing references from vec.iter

    for x in v.iter() {
        println!("x: {}", x);
    }
    
  2. Printing dereferenced elements from vec.iter

    for x in v.iter() {
        println!("x: {}", *x);
    }
    
  3. 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 */ })
}

Source code Reference


Solution

  • 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.