That rewrite is inaccurate since different &mut T arguments can be used on disjunct places while tuples will introduce its own places (and necessitate more moves). So while it is similarly infections at least the guard version is semantically close to the intent of taking ownership from mutable arguments. A guard may work better with init expressions in the sense of composing rather than also leaking the re-initialization method but it is quite early and I've not followed that implementation too closely to say for sure.
This is possible with declarative attribute macros on nightly.
FWIW, replace_with is also unsound in combination with the Nomicon's recommended way of representing opaque FFI types. The correct solution to both is for extern type to become a thing that can't be owned by value.
It's also a pain for mem::swap, which biases me towards ZST "fakes" being a cute trick but breaking standard Rust assumptions.
Disclaimer: I am a member of the UCG and T-opsem, but am speaking solely for myself, not the team(s).
What exactly is unsound, do you have an example in mind? If the C type is opaque and the Rust representation is (as an implementation detail) a ZST, then obtaining ownership doesn't seem particularly useful to me. Especially if all of the APIs operate on [*mut|&mut] Opaque instead of Opaque.
Except now any code using &Opaque now thinks the address is of your owned value, not the address that the FFI code handed out and is expecting to get back.
Without modifying your data structure the following seems sufficiently elegant.
fn push_front(&mut self, x:T) {
*self = Self::Cons((x, std::mem::replace(self, Self::Nil)).into());
}
and for a bit more clarity...
fn push_front(&mut self, x:T) {
let default = Self::Nil;
let current_head = std::mem::replace(self, default);
*self = Self::Cons((x, current_head).into())
}
The fact a valid value must always exist in the target of a reference makes the std::mem module more important than the documentation would suggest, at least back when I read it many moons ago.
Thinking about this some more, the Nomicon only recommends *mut Opaque, it does not say anything about &mut Opaque.
If references are desired over raw pointers, then Pin<&mut Opaque> better expresses the intent that opaque structs cannot be moved (except through blessed FFI escape hatches) and can not be created on the stack by downstream users.
Thatâs basically just a more convoluted way of doing the following[1], isnât it? ![]()
though I guess, yours might be the ârightâ way if â for whatever reason â a Default implementation wasnât actually desired ^^
and not really an improvement over whatâs already mentioned in this thread âŠď¸
Your approach to introducing a default variant aligns with my personal preference. I like to embed as many constraints as possible directly into the type definitions, thereby centralizing them in a definitive way.
My post was primarily instructional in nature. In particular, I hadnât fully appreciated the importance of the std::mem module in working within the constraints imposed by Rustâs ownership model.