So can someone help me understand what's going on in my original example? I understand that the Vec<u8>
represents the bytes of the file and that each byte is made up of 8 bits (and that there's one byte for each character in the file). So the read
function loads a byte for each character into a vector that I can index into.
Correct
However, when I call println!
, what gets printed out appears to be ASCII decimal values.
Yes
Is there some conversion going on under the hood?
Yeah, basically. These bytes (u8
) represent each character in your file, and they are encoded as ASCII. But the rust data type is a u8
, an unsigned 8-bit integer. So even though they are intended in the file to represent ASCII, your code will print them as it would print any other integer: by converting it into a decimal string.
Just like if you said, let i: i32 = 314; println!("{}", i);
, it would take the 32-digit base-2 representation of i
, and convert it to a base-10 string.
Does this conversion only take place when you call println!
or other functions that are intended to display the bytes?
Hmm, well, the conversion from u8
to text is happening in u8
's implementation of std::fmt::Display
. The various formatting macros in the standard library, and in other libraries such as logging libraries for example, use the various traits in std::fmt
to convert from data types to text, mainly Display
and Debug
.
The following formatting macros exist in the standard library, and they can format text:
print!
(prints to stdout)
println!
(prints to stdout with newline character)
eprint!
(prints to stderr)
eprintln!
(prints to stderr with newline character)
write!
(writes to whatever output stream you give it)
writeln!
(writes to whatever output stream you give it)
format!
(creates a String
)
They use the "{}"
syntax to format with the std::fmt::Display
trait, the "{:?}"
and "{:#?}"
syntax to format with the std::fmt::Debug
trait, and other lesser known syntaxes to format with various other flags and traits, like hex formatting.
The way they achieve this cool and consistent formatting syntax is that they all delegate to the format_args!
macro, which produces a value which borrows its args and implements Display
, representing the formatted string.
For example, these would all do the same thing:
println!("{} + 5 == {}", a, a + 5)
println!("{}", format_args!("{} + 5 == {}", a, a + 5))
println!("{}", format_args!("{}", format_args!("{} + 5 == {}", a, a + 5)))
But I worry that I'm getting carried away. Hopefully this clarifies.