0

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

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
mattgathu
  • 1,129
  • 1
  • 19
  • 28

1 Answers1

4

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.

Silentroar
  • 71
  • 1
  • 9
loganfsmyth
  • 156,129
  • 30
  • 331
  • 251
  • >> As expected, the i32 type implements Display, which is what allows your case #1 and case #2 to work. Because they are getting a standard i32 value, not a reference, it all works.<< Do you mean case #2 and #3? Case #1 is a reference – mattgathu Jul 15 '17 at 04:14
  • Sorry 100% typoed the case numbers. – loganfsmyth Jul 15 '17 at 04:16
  • I am still lost. Ok, there's automatic blanket implementation of Display trait for &T, right? But where does it says that println! should dereference references and print out values? I still don't see the logic while rereading this answer :( – František Heča Aug 22 '21 at 13:15