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]
  4. use core::fmt::{self, Write};
  5. #[allow(unused_imports)]
  6. use aux11::{entry, iprint, iprintln, usart1};
  7. macro_rules! uprint {
  8. ($serial:expr, $($arg:tt)*) => {
  9. $serial.write_fmt(format_args!($($arg)*)).ok()
  10. };
  11. }
  12. macro_rules! uprintln {
  13. ($serial:expr, $fmt:expr) => {
  14. uprint!($serial, concat!($fmt, "\n"))
  15. };
  16. ($serial:expr, $fmt:expr, $($arg:tt)*) => {
  17. uprint!($serial, concat!($fmt, "\n"), $($arg)*)
  18. };
  19. }
  20. struct SerialPort {
  21. usart1: &'static mut usart1::RegisterBlock,
  22. }
  23. impl fmt::Write for SerialPort {
  24. fn write_str(&mut self, s: &str) -> fmt::Result {
  25. // TODO implement this
  26. // hint: this will look very similar to the previous program
  27. Ok(())
  28. }
  29. }
  30. #[entry]
  31. fn main() -> ! {
  32. let (usart1, mono_timer, itm) = aux11::init();
  33. let mut serial = SerialPort { usart1 };
  34. uprintln!(serial, "The answer is {}", 40 + 2);
  35. loop {}
  36. }