Niko raises an interesting point about unsafe
in the ongoing discussion about await
:
This makes me wonder if a variant postfix syntax for unsafe
that takes an expression rather than a block might be useful; building on that, as an analogy to futures, I wonder if it might be reasonable to support a monad-like way of performing unsafe
operations as some kind of first-class citizen, permitting the creation of special unsafe
closures.
An example
My knee-jerk response is that this would not be a good idea, because it would obscure unsafe
code somewhat, and because it would separate the unsafe
code itself from the location where the keyword is used. But just as a thought experiment, here's what it could look like, introducing a dummy keyword and type:
fn read_mem(ptr: *const u32) -> Unsafe<Fn()->u32> {
ptr.unsafe(core::ptr::read)
}
Here, unsafe
is a postfix keyword (maybe?) that creates an object of type Unsafe
. The keyword operates on an expression and takes a callable as an argument; the callable must be able to accept the expression as an argument. Unsafe
itself is the "unsafe monad", which supports an interface something like this:
fn get_val(op: Unsafe<impl Fn()->u32>) -> u32 {
op.do_unsafe
}
Here, do_unsafe
is a postfix keyword, like .await
.
Note, of course, that Unsafe<FnOnce>
and Unsafe<FnMut>
would also exist.
If the user wanted to execute the unsafe
bit immediately instead of constructing an Unsafe
object and postponing the unsafe
operation, then the keywords could be used in conjunction, like so:
ptr.unsafe(std::mem::read).do_unsafe
Here is some Playground code demonstrating, essentially, the "desugared" version of the above example (with an extra unsafe
that wouldn't be necessary if this feature were adopted).