I have a Result<Vec<f64>, _>
. When I try to extract a pointer to the actual f64
array, what I observe is that the array dptr
points to is a corrupted version of the expected array (the first 10 bytes have been changed).
Why does this happen, and how I can avoid it?
use std::error::Error;
fn main() {
let res: Result<Vec<f64>, Box<dyn Error>> = Ok(vec![1., 2., 3., 4.]);
let dptr: *const f64 = match res {
Ok(v) => &v[0],
Err(_) => std::ptr::null(),
};
assert_eq!(unsafe { *dptr }, 1.0);
}
Result:
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `0.0`,
right: `1.0`', src/main.rs:9:5
CodePudding user response:
The behaviour of that program is undefined, which can be seen by running it under Miri, which is a Rust interpreter that can sometimes detect Undefined Behavior. (you can do this in the playground by clicking "Tools" (top right) -> "Miri"):
error: Undefined Behavior: pointer to alloc1039 was dereferenced after this allocation got freed
--> src/main.rs:9:25
|
9 | assert_eq!(unsafe { *dptr }, 1.0);
| ^^^^^ pointer to alloc1039 was dereferenced after this allocation got freed
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
What's happening here is a use-after-free: the Ok(v) => &v[0],
line moves data from v
(and thus res
), causing it to be freed. Later, data used for other variable overwrote the existing data (because using a pointer after the memory it points to is freed is Undefined Behaviour).
If you had tried to read the data out of the res
the normal way without unsafe
, you would have got a compile-time error for this very issue:
error[E0382]: use of partially moved value: `res`
--> src/main.rs:9:10
|
6 | Ok(v) => &v[0],
| - value partially moved here
...
9 | dbg!(res.unwrap()[0]);
| ^^^ value used here after partial move
|
= note: partial move occurs because value has type `Vec<f64>`, which does not implement the `Copy` trait
help: borrow this field in the pattern to avoid moving `res.0`
|
6 | Ok(ref v) => &v[0],
|