uprintln!

For the next exercise, we’ll implement the uprint! family of macros. Your goal is to make thisline of code work:

1. uprintln!(serial, "The answer is {}", 40 + 2);

Which must send the string "The answer is 42" through the serial interface.

How do we go about that? It’s informative to look into the std implementation of println!.

1. // src/libstd/macros.rs
2. macro_rules! print {
3. ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*)));
4. }

Looks simple so far. We need the built-in format_args! macro (it’s implemented in the compiler so wecan’t see what it actually does). We’ll have to use that macro in the exact same way. What does this_print function do?

1. // src/libstd/io/stdio.rs
2. pub fn _print(args: fmt::Arguments) {
3. let result = match LOCAL_STDOUT.state() {
4. LocalKeyState::Uninitialized |
5. LocalKeyState::Destroyed => stdout().write_fmt(args),
6. LocalKeyState::Valid => {
7. LOCAL_STDOUT.with(|s| {
8. if s.borrow_state() == BorrowState::Unused {
9. if let Some(w) = s.borrow_mut().as_mut() {
10. return w.write_fmt(args);
11. }
12. }
13. stdout().write_fmt(args)
14. })
15. }
16. };
17. if let Err(e) = result {
18. panic!("failed printing to stdout: {}", e);
19. }
20. }

That looks complicated but the only part we are interested in is: w.write_fmt(args) andstdout().write_fmt(args). What print! ultimately does is call the fmt::Write::write_fmt methodwith the output of format_args! as its argument.

Luckily we don’t have to implement the fmt::Write::write_fmt method either because it’s a defaultmethod. We only have to implement the fmt::Write::write_str method.

Let’s do that.

This is what the macro side of the equation looks like. What’s left to be done by you is provide theimplementation of the write_str method.

Above we saw that Write is in std::fmt. We don’t have access to std but Write is alsoavailable in core::fmt.

1. #![deny(unsafe_code)]
2. #![no_main]
3. #![no_std]

5. use core::fmt::{self, Write};

7. #[allow(unused_imports)]
8. use aux11::{entry, iprint, iprintln, usart1};

10. macro_rules! uprint {
11. ($serial:expr, $($arg:tt)*) => {
12. $serial.write_fmt(format_args!($($arg)*)).ok()
13. };
14. }

16. macro_rules! uprintln {
17. ($serial:expr, $fmt:expr) => {
18. uprint!($serial, concat!($fmt, "\n"))
19. };
20. ($serial:expr, $fmt:expr, $($arg:tt)*) => {
21. uprint!($serial, concat!($fmt, "\n"), $($arg)*)
22. };
23. }

25. struct SerialPort {
26. usart1: &'static mut usart1::RegisterBlock,
27. }

29. impl fmt::Write for SerialPort {
30. fn write_str(&mut self, s: &str) -> fmt::Result {
31. // TODO implement this
32. // hint: this will look very similar to the previous program
33. Ok(())
34. }
35. }

37. #[entry]
38. fn main() -> ! {
39. let (usart1, mono_timer, itm) = aux11::init();

41. let mut serial = SerialPort { usart1 };

43. uprintln!(serial, "The answer is {}", 40 + 2);

45. loop {}
46. }