Skip to content
Merged
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
36 changes: 14 additions & 22 deletions src/idiomatic/leveraging-the-type-system/raii/drop_option.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,55 +21,47 @@ impl File {
}

fn write(&mut self, data: &str) -> std::io::Result<()> {
// We have to go through the `Option` to get the `Handle`
// before we can use it.
let handle = self.0.as_ref().unwrap();
println!("write '{data}' to file '{}'", handle.path);
Ok(())
}

fn close(mut self) -> std::io::Result<&'static str> {
Ok(self.0.take().unwrap().path)
}
}

impl Drop for File {
fn drop(&mut self) {
if let Some(handle) = self.0.take() {
println!("automatically closing handle for file: {}", handle.path);
}
let handle = self.0.take().unwrap();
handle.close();
}
}

struct Handle {
path: &'static str,
}
impl Drop for Handle {
fn drop(&mut self) {
println!("closed handle for file: {}", self.path)

impl Handle {
fn close(self) {
println!("Closing {}", self.path);
}
}

fn main() -> std::io::Result<()> {
let mut file = File::open("foo.txt")?;
file.write("hello")?;
println!("manually closed file: {}", file.close()?);
Ok(())
}
```

<details>

- In this example we want to let the user call `close()` manually so that errors
from closing the file can be reported explicitly.

- At the same time we still want RAII semantics: if the user forgets to call
`close()`, the handle must be cleaned up automatically in `Drop`.

- Wrapping the handle in an `Option` gives us both behaviors. `close()` extracts
the handle with `take()`, and `Drop` only runs cleanup if a handle is still
present.
- In this example we want to call `close` on the inner `Handle` in our `Drop`
impl, but `close` requires ownership of the `Handle`. We can't do this
normally, because we don't get ownership of the `File` object in `drop`, and
therefore can't move out of the field.

Demo: remove the `.close()` call and run the code — `Drop` now prints the
automatic cleanup.
- Wrapping the handle in an `Option` gives us a way to move out of the field
through a mutable reference.

- The main downside is ergonomics. `Option` forces us to handle both the `Some`
and `None` case even in places where, logically, `None` cannot occur. Rust’s
Expand Down