Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate Writer::write_string and fmt::Write::write_str methods #417

Merged
merged 1 commit into from
Mar 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions blog/content/second-edition/posts/03-vga-text-buffer/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ To print whole strings, we can convert them to bytes and print them one-by-one:

```rust
impl Writer {
pub fn write_str(&mut self, s: &str) {
pub fn write_string(&mut self, s: &str) {
for byte in s.bytes() {
self.write_byte(byte)
}
Expand All @@ -221,7 +221,7 @@ pub fn print_something() {
};

writer.write_byte(b'H');
writer.write_str("ello");
writer.write_string("ello");
}
```
It first creates a new Writer that points to the VGA buffer at `0xb8000`. The syntax for this might seem a bit strange: First, we cast the integer `0xb8000` as an mutable [raw pointer]. Then we convert it to a mutable reference by dereferencing it (through `*`) and immediately borrowing it again (through `&mut`). This conversion requires an [`unsafe` block], since the compiler can't guarantee that the raw pointer is valid.
Expand Down Expand Up @@ -315,23 +315,21 @@ impl Writer {
Instead of a normal assignment using `=`, we're now using the `write` method. This guarantees that the compiler will never optimize away this write.

### Formatting Macros
It would be nice to support Rust's formatting macros, too. That way, we can easily print different types like integers or floats. To support them, we need to implement the [core::fmt::Write] trait. The only required method of this trait is `write_str` that looks quite similar to our `write_str` method. To implement the trait, we just need to move it into an `impl fmt::Write for Writer` block and add a return type:
It would be nice to support Rust's formatting macros, too. That way, we can easily print different types like integers or floats. To support them, we need to implement the [`core::fmt::Write`] trait. The only required method of this trait is `write_str` that looks quite similar to our `write_string` method, just with a `fmt::Result` return type:

[core::fmt::Write]: https://doc.rust-lang.org/nightly/core/fmt/trait.Write.html
[`core::fmt::Write`]: https://doc.rust-lang.org/nightly/core/fmt/trait.Write.html

```rust
use core::fmt;

impl fmt::Write for Writer {
fn write_str(&mut self, s: &str) -> fmt::Result {
for byte in s.bytes() {
self.write_byte(byte)
}
self.write_string(s);
Ok(())
}
}
```
The `Ok(())` is just a `Ok` Result containing the `()` type. We can drop the `pub` because trait methods are always public.
The `Ok(())` is just a `Ok` Result containing the `()` type.

Now we can use Rust's built-in `write!`/`writeln!` formatting macros:

Expand Down
20 changes: 12 additions & 8 deletions src/vga_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,17 @@ impl Writer {
}
}

/// Writes the given ASCII string to the buffer.
///
/// Wraps lines at `BUFFER_WIDTH`. Supports the `\n` newline character. Does **not**
/// support strings with non-ASCII characters, since they can't be printed in the VGA text
/// mode.
fn write_string(&mut self, s: &str) {
for byte in s.bytes() {
self.write_byte(byte)
}
}

/// Shifts all lines one line up and clears the last row.
fn new_line(&mut self) {
for row in 1..BUFFER_HEIGHT {
Expand All @@ -125,15 +136,8 @@ impl Writer {
}

impl fmt::Write for Writer {
/// Writes the given ASCII string to the buffer.
///
/// Wraps lines at `BUFFER_WIDTH`. Supports the `\n` newline character. Does **not**
/// support strings with non-ASCII characters, since they can't be printed in the VGA text
/// mode.
fn write_str(&mut self, s: &str) -> fmt::Result {
for byte in s.bytes() {
self.write_byte(byte)
}
self.write_string(s);
Ok(())
}
}
Expand Down