Say, I want to report result and execution time of some operation. I can use struct for convenience. It rememebers starting time and reports result:
struct Metric {
start: std::time::Instant,
}
impl Metric {
pub fn new() -> Self {
Metric { start: std::time::Instant::now() }
}
pub fn ok(self) {
send_metric("ok", self.start.elapsed());
}
pub fn error(self) {
send_metric("error", self.start.elapsed());
}
}
fn main() {
let metric = Metric::new();
// …
metric.ok();
}
However, it is easy to forget to call either ok()
or error()
:
fn main() {
let metric = Metric::new();
if (something()) {
return; // ooops
}
// …
metric.ok();
}
It is possible to overcome this problem by wrapping function into another one which returns result, because returning is forced by compiler:
fn main() -> Result<(), ()> {
let metric = Metric::new();
match do_main() {
Result::Ok(()) => metric.ok(),
Result::Err(()) => metric.err(),
}
}
fn do_main() {
if (something()) {
return Ok(()); // OK
}
// …
return Ok(());
}
However, this is verbose and not very convenient, especially when you want to cover just some part of function body. In this case you have to extract part to new function or turn it into closure, and then you have problems with early returns.
I have idea which may help to solve this problem beautifully: “undroppable” types. Well, they are not really undroppable at all, but they can be dropped from inside their own methods only, not from outside:
struct Metric {
start: std::time::Instant,
}
// Somehow mark struct as undroppable, syntax doesn't matter now, let it be:
impl !Drop for Metric;
impl Metric {
pub fn new() -> Self {
Metric { start: std::time::Instant::now() }
}
pub fn ok(self) {
send_metric("ok", self.start.elapsed());
// OK, metric is dropped here
}
pub fn error(self) {
send_metric("error", self.start.elapsed());
// OK, metric is dropped here
}
}
fn main() {
let metric = Metric::new();
// …
if (something()) {
return; // Error: `metric` struct created at … can't be dropped,
// invoke method which accepts `self` by value: `ok`, `error`.
}
metric.ok();
}
What do you thing?