If we don't have guaranteed RAII, can we have finally


#21

Example: compare

foo:
	// unconditionally (tail-)calls the `call_if_true` function
	jmp	playground::ABool::call_if_true

with

foo:
	testb	%dil, %dil
	// if input bool is false jump to return
	je	.LBB5_1
	jmp	<playground::ABool<B>>::call_if_true

.LBB5_1:
	retq

#22

That’s an incorrect formulation of the pattern. You’re providing a runtime argument to foo.

The correct analogue would be foo::<False>() instead of foo(false), which you will find monomorphizes to just the one branch. You can’t recover compile-time information once it’s removed.

Unless you’re saying that’s how scope-guard has implemented it? In which case they’ve made a mistake and (should) can update their macros to use compile-time type arguments.


#23

You are right, my examples do not illustrate my point that well. I wanted to show that thanks to monomorphisation the False::call_if_true(closure) was successfully replaced by a no op even despite de #[inline(never)]. Had I not used a runtime argument, rustc would have been able to optimise both examples ^^’

But don’t worry, ::scope_guard does define things like defer! <-> ScopeGuard<Always> with no runtime arguments.


#24

I thought again my senario of network protocal handling. I believe this is exactly what would help. The code in do_something or do_something_else might mutate local states, espacially, provide data needed for the final package. Therefore, to generate the final package we have to take ownership of relevent data in only the cleanup time, not anytime earlier.

By carefully design the data structure, including using Options and Results, or even Cells and Rcs, we could do the same without language support. However, a proper language support will make it easier to write and read, at the very least.


#25

I think the proper way to do this is to create a new type that implements Drop.

As far as I can tell, this is strictly more powerful than the defer approach because both the Drop-implementing type and the closure will have to capture the data or a reference to the data, but the Drop-implementing type lets you have an &mut to the captured data in the meantime, while the closure does not.


#26

IIRC the finally in Object Pascal implementations is fully guaranteed at the language level at least (as is the deallocation of resources if an exception is raised in a constructor.) There’s still of course the unvoidable “power failure” scenario, though.


#27

The other one I was thinking of, which also came up in the leakpocalypse, is that an infinite loop inside the try will prevent the finally from running. I also wouldn’t be surprised if things like stack overflows didn’t run finallies, but don’t know for sure in Object Pascal.


#28

There have been situations in which I’ve wanted something like Java’s anonymous classes, in which I create a brand new type in one expression for the sole purpose of implementing an interface. I’d like something like

let x = 0;
// Pretend this syntax isn't ambiguous with items for one second.
let foo = impl Drop { 
  fn drop(&mut self) {
    x += 1;
  }
};

where the function implementations have the same capture behavior as closures; the contents of the type are determined by the captures of all the included functions. There is, unfortunately, the rather gross detail that no other fn in Rust captures, so we’d probably want drastically different syntax.


#29

That’s exactly what the crate I was about to publish allowed to write:

use ::call_on_drop::*;
let mut x = 0;
let foo = CallOnDrop::new(|| x = 42);
let ret = this_may_panic();
// uncomment the following if you want the closure to only be called in case of panic/unwinding
// foo.drop_silently();
ret

While also providing a macro:

use ::call_on_drop::*;
let mut x = 0;
may_panic!({
  this_may_panic()
} finally {
  x = 42
})

But, again, that’s what ::scopeguard already does, and on top of that it does it better, since it allows to solve the mut ref problem with an explicit environment capture that allows the guard to DerefMut into it, allowing to write something along these lines:

let mut x: i32 = 0;
let ret = {
/* to work with x, a mutable reference suffices,
so we give an exclusive ref to the Drop struct,
and shadow the unusable x with a mutable reference */
  let x: &mut i32 = *with_Drop(&mut x, |slf| *slf = 42);
  this_may_panic(&mut *x)
};
assert_eq!(x, 42_i32);
ret