我一直在阅读《Rust Book》,在有关 Unsafe Rust 的部分中,它展示了如何实现该split_at_mut方法的简单版本。给定的实现是:
use std::slice;
fn split_at_mut(values: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
let len = values.len();
let ptr = values.as_mut_ptr();
assert!(mid <= len);
unsafe {
(
slice::from_raw_parts_mut(ptr, mid),
slice::from_raw_parts_mut(ptr.add(mid), len - mid),
)
}
}
Run Code Online (Sandbox Code Playgroud)
但是,我想出了这个:
fn split_at_mut(values: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
let len = values.len();
assert!(mid <= len);
let begin = &mut values[..mid] as *mut [i32];
let end = &mut values[mid..] as *mut [i32];
unsafe {
(&mut *begin, &mut *end)
}
}
Run Code Online (Sandbox Code Playgroud)
它产生(据我所知)相同的结果,但在我看来更容易阅读。
那么是什么让给定的解决方案比我的“更好”呢?我的解决方案是否存在安全(或其他)问题?事实上,给定的解决方案更容易阅读吗?是不是要把这个方法扩展到其他切片上?与编码风格和“良好实践”有关吗?
期待您的答复
以下是我认为为什么(正如另一个答案所写)Miri 拒绝您的代码的原因:
\n(\n slice::from_raw_parts_mut(ptr, mid),\n slice::from_raw_parts_mut(ptr.add(mid), len - mid),\n)\nRun Code Online (Sandbox Code Playgroud)\n&mut在这种情况下,您将根据来自单个&mut引用的指针构造两个不重叠的引用values。
let begin = &mut values[..mid] as *mut [i32];\nlet end = &mut values[mid..] as *mut [i32];\n \nunsafe {\n (&mut *begin, &mut *end)\n}\nRun Code Online (Sandbox Code Playgroud)\n在这种情况下,您正在构造两个临时可变引用,这两个引用都借用values(因此不能同时存在),然后 \xe2\x80\x9cresurrecting\xe2\x80\x9d (不是技术术语)它们来自指针。
下面是相同代码的一个版本,它扩展了一些临时变量和语法糖:
\nuse std::ops::IndexMut;\n\nlet begin = {\n let v1 = &mut values;\n IndexMut::index_mut(v1, ..mid) as *mut [i32]\n};\nlet end = {\n let v2 = &mut values;\n IndexMut::index_mut(v2, mid..) as *mut [i32]\n};\n\nunsafe { (&mut *begin, &mut *end) }\nRun Code Online (Sandbox Code Playgroud)\n您的两个指针begin和end是从v1和派生的,因此它们只有在和是的v2情况下才有效,但是和彼此冲突。v1v2v1v2
话虽如此,我不能肯定地说允许这个程序是否可以,因为毕竟它不会创建任何实际的可变别名。我绝对没有受过 Miri 检查的 Stacked Borrows 模型的理论基础的教育。
\n