These are two separate language features, but they should be discussed together because each one is not that useful on its own but together they can be extremely useful.
The first is postfix let
which works like this:
foo.let bar {
baz()
}
Is syntactic sugar for:
{
let bar = foo;
baz()
}
Pretty simple, but not very useful (though it will bring some joy to those who request pipelining functions / operators). It does, however, enable the next feature I'm going to suggest:
Postfix macros. A feature that was suggested and discussed many times before, but always had the problem of "capturing" the expression it operates on. In foo().bar().baz!()
, how would baz!
receive foo().bar()
as parameter? And won't it be surprising and confusing that the macro can modify syntax written outside its body? But if it acted like a normal macro and only emit tokens that'll take its syntactical spot in the token stream it won't be that useful since all it'll be able to do is slap methods calls and fill their arguments.
But if we have postfix let
, postfix macros could behave like regular macros and only replace theirselves, but still be useful because a postfix let
is not a closure so it allows flow control!
For example, if we look at old try!
(ignoring that it is now a keyword), we could write it as a postfix macro like so:
macro_rules! .try {
() => (.let value {
match value {
$crate::result::Result::Ok(val) => val,
$crate::result::Result::Err(err) => {
return $crate::result::Result::Err($crate::convert::From::from(err))
}
}
});
}
Or await!
, when the idea was to have it as a macro and base async/await on generators:
macro_rules! await {
() => (.let mut future {
let future = &mut future;
// The above borrow is necessary to force a borrow across a
// yield point, proving that we're currently in an immovable
// generator, making the below `Pin::new_unchecked` call
// safe.
loop {
let poll = ::futures::__rt::in_ctx(|ctx| {
let pin = unsafe {
::futures::__rt::std::mem::Pin::new_unchecked(future)
};
::futures::__rt::StableFuture::poll(pin, ctx)
});
// Allow for #[feature(never_type)] and Future<Error = !>
#[allow(unreachable_code, unreachable_patterns)]
match poll {
::futures::__rt::std::result::Result::Ok(::futures::__rt::Async::Ready(e)) => {
break ::futures::__rt::std::result::Result::Ok(e)
}
::futures::__rt::std::result::Result::Ok(::futures::__rt::Async::Pending) => {}
::futures::__rt::std::result::Result::Err(e) => {
break ::futures::__rt::std::result::Result::Err(e)
}
}
yield ::futures::__rt::Async::Pending
}
})
}
Which will allow foo().await!().try!()
without designated syntax (which is still a good idea in such common cases, but it's also good to be able to create macros for the less common but still common cases)