I've spent quite a long time thinking about strong updates, and was considering writing a proposal for them, when I stumbled across this thread and saw that it suggested something very similar.
The primary difference with my planning is that the types work somewhat differently, using place generics @a that allow you to specify that two references reference the same place (possibly with different types), allowing you to write function signatures like
fn new_in_place<@a: MaybeUninit<Foo>>(foo: &@a mut MaybeUninit<Foo>)
-> &@a mut Foo
The caller of this function would then be able to change the type of their local variable (in the example in the OP, foo) by consuming the resulting reference (but they could also just use the resulting &@a mut Foo as a normal &mut Foo). The place generic is needed so that the caller can know that they're transmuting the correct place, rather than some other place that the function happened to have a reference to. (The place generic also contains the functionality of a lifetime generic – the lifetimes are calculated as though @a were 'a – because in practice the two will always be combined.)
As in this example, I'd noticed that this is only type-safe for dropping if the type of the place is always able to hold the current type of the variable – to allow arbitrary type conversions in arbitrary places, you'd have to prevent the new type of reference being dropped early (including during panic-unwinds), just as was discussed in this thread. However, it'd probably be OK to just restrict &@a T to hold types T that were subtypes of the storage type of @a, because it can safely be very general (e.g. a big union, or MaybeUninit which can hold anything that fits in a size/alignment sense): in that case, if the reference gets dropped unexpectedly, the referenced object gets leaked but there are no soundness issues.
One big reason I wanted the references to be separate on the input and output is that you may want to write functions that conditionally change the type of an argument. For example, imagine a signature like
fn try_new<@a: MaybeUninit<Foo>(out: &@a mut MaybeUninit<Foo>)
-> Option<&@a Foo>`
This way, if try_new succeeds, you can know that your Foo is initialised, but if it fails, you don't get that promise. This works for conditional destruction, too, e.g. an "nonempty iterator" API could look like
fn next<@a: MaybeUninit<MyIterator>>(&@a mut MyIterator)
-> (MyIterator::Item, Option<&@a mut MyIterator>)
which only gives you the ability to get at the next item if there actually is a next item.
In any case, I don't have a complete design for this and the syntax could probably do with cleaning up and simplifying (this syntax is somewhat ugly and pretty verbose), but I thought I'd share my thoughts on this so far as I think strong updates would be a great addition to the language, and maybe my half-finished design helps to inspire someone else.