Drop: Option
// Copyright 2025 Google LLC // SPDX-License-Identifier: Apache-2.0 struct File(Option<Handle>); impl File { fn open(path: &'static str) -> std::io::Result<Self> { Ok(Self(Some(Handle { path }))) } 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(()) } } impl Drop for File { fn drop(&mut self) { let handle = self.0.take().unwrap(); handle.close(); } } struct Handle { path: &'static str, } impl Handle { fn close(self) { println!("Closing {}", self.path); } } fn main() -> std::io::Result<()> { let mut file = File::open("foo.txt")?; file.write("hello")?; Ok(()) }
-
In this example we want to call
closeon the innerHandlein ourDropimpl, butcloserequires ownership of theHandle. We can’t do this normally, because we don’t get ownership of theFileobject indrop, and therefore can’t move out of the field. -
Wrapping the handle in an
Optiongives us a way to move out of the field through a mutable reference. -
The main downside is ergonomics.
Optionforces us to handle both theSomeandNonecase even in places where, logically,Nonecannot occur. Rust’s type system cannot express that relationship betweenFileand itsHandle, so we handle both cases manually.
More to explore
Instead of Option we could use
ManuallyDrop,
which suppresses automatic destruction by preventing Rust from calling Drop
for the value; you must handle teardown yourself.
The scopeguard example on the previous slide shows how
ManuallyDrop can replace Option to avoid handling None in places where the
value should always exist.
In such designs we typically track the drop state with a separate flag next to
the ManuallyDrop<Handle>, which lets us track whether the handle has already
been manually consumed.