Consider the code:
use std::mem::MaybeUninit;
struct Foo {
buffer: MaybeUninit<[u8; 32]>,
}
fn call_device(_p: *mut [u8; 32]) {
//....
}
fn do_magic(mut f: Foo) -> Foo {
// Pass f.buffer to hardware device modifying it's content
// ...
call_device(f.buffer.as_mut_ptr());
f // Return it back, moving
}
fn main() {
let foo = Foo {
buffer: MaybeUninit::uninit(),
};
let new_foo = do_magic(foo); // <--- foo moved
let result = unsafe { new_foo.buffer.assume_init() };
println!("{:?}", result)
}
Here, as commented, the do_magic()
function is passing the buffer to some device which is populating it with some values. But apparently it is not visible to Rust. Will it still preserve the value between the moves of Foo
and the result
will contain the value written by the device, or there is a chance that it will get optimized? If there is, how to prevent it (other than prevent moving by using pinned references and such)?
Consider the code:
use std::mem::MaybeUninit;
struct Foo {
buffer: MaybeUninit<[u8; 32]>,
}
fn call_device(_p: *mut [u8; 32]) {
//....
}
fn do_magic(mut f: Foo) -> Foo {
// Pass f.buffer to hardware device modifying it's content
// ...
call_device(f.buffer.as_mut_ptr());
f // Return it back, moving
}
fn main() {
let foo = Foo {
buffer: MaybeUninit::uninit(),
};
let new_foo = do_magic(foo); // <--- foo moved
let result = unsafe { new_foo.buffer.assume_init() };
println!("{:?}", result)
}
Here, as commented, the do_magic()
function is passing the buffer to some device which is populating it with some values. But apparently it is not visible to Rust. Will it still preserve the value between the moves of Foo
and the result
will contain the value written by the device, or there is a chance that it will get optimized? If there is, how to prevent it (other than prevent moving by using pinned references and such)?
1 Answer
Reset to default 0Yes, this code is sound as long as call_device()
does properly initialize the array, for example:
fn call_device(p: *mut [u8; 32]) {
for i in 0..32 {
unsafe { p.cast::<u8>().add(i).write(42) };
}
}
Running the whole thing through the Miri interpreter reveals no issues. You can try it here (click Tools -> Miri).
However, in Rust it would be much more idiomatic for do_magic()
to accept a &mut Foo
, so you could use the function even if you weren't the owner of the Foo
. It makes the code much simpler as well.
fn do_magic(f: &mut Foo) { // no need to worry about returning anything
call_device(f.buffer.as_mut_ptr());
}
f
, it cannot be optimized. Unless you pass it by shared reference, in which case the compiler may assume it is not mutated. – Chayim Friedman Commented Mar 19 at 22:32f
- if you didn't get your pointer to-be-written fromas_mut_ptr
(or similar), then the compiler may assume no writes occurred. – kmdreko Commented Mar 19 at 22:43mut
pointer). When you say "The move just copies what was there, but what was there was changed" - you mean the copy will happen always when moving and the result in this case will get the expected value? – Eugene Sh. Commented Mar 19 at 22:45