Note that if manually closing the handle (e.g., because it requires some additional parameter) is paramount, then the code can be refactored into CPS with a type-level “proof-of-work” to ensure the user does close it (unless the code panics):
/// instead of:
#[cfg(any())]
mod lib {
pub
struct Handle (());
impl Handle {
pub
fn new () -> Self
{
Self(())
}
pub
fn close (self, some_param: i32)
{
println!("Handle was correctly closed with {}", some_param);
}
}
}
/// do this
mod lib {
pub
struct Handle (());
pub
struct ClosedHandle (());
impl Handle {
pub(self) // private
fn new () -> Self
{
Self(())
}
pub
fn with_new (f: impl FnOnce(Handle) -> ClosedHandle)
{
f(Self::new());
}
pub fn close (self, some_param: i32) -> ClosedHandle
{
println!("Handle was correctly closed with {}", some_param);
ClosedHandle(())
}
}
}
fn main ()
{
use lib::Handle;
Handle::with_new(|handle| {
handle.close(42)
});
}
If the .close(42)
line is uncommented, we get:
error[E0308]: mismatched types
--> src/main.rs:55:31
|
55 | Handle::with_new(|handle| {
| _______________________________^
56 | | // handle.close(42)
57 | | });
| |_____^ expected struct `lib::ClosedHandle`, found ()
|
= note: expected type `lib::ClosedHandle`
found type `()`
It does become more cumbersome to use, but then again, there cannot be that many things whose closing logic cannot be covered by Drop
.