split_at_mut 的 Rust 实现

cli*_*ino 4 rust

我一直在阅读《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)

它产生(据我所知)相同的结果,但在我看来更容易阅读。

那么是什么让给定的解决方案比我的“更好”呢?我的解决方案是否存在安全(或其他)问题?事实上,给定的解决方案更容易阅读吗?是不是要把这个方法扩展到其他切片上?与编码风格和“良好实践”有关吗?

期待您的答复

Kev*_*eid 5

以下是我认为为什么(正如另一个答案所写)Miri 拒绝您的代码的原因:

\n
(\n    slice::from_raw_parts_mut(ptr, mid),\n    slice::from_raw_parts_mut(ptr.add(mid), len - mid),\n)\n
Run Code Online (Sandbox Code Playgroud)\n

&mut在这种情况下,您将根据来自单个&mut引用的指针构造两个不重叠的引用values

\n
let begin = &mut values[..mid] as *mut [i32];\nlet end = &mut values[mid..] as *mut [i32];\n    \nunsafe {\n    (&mut *begin, &mut *end)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

在这种情况下,您正在构造两个临时可变引用,这两个引用都借用values(因此不能同时存在),然后 \xe2\x80\x9cresurrecting\xe2\x80\x9d (不是技术术语)它们来自指针。

\n

下面是相同代码的一个版本,它扩展了一些临时变量和语法糖:

\n
use 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) }\n
Run Code Online (Sandbox Code Playgroud)\n

您的两个指针beginend是从v1和派生的,因此它们只有在和是的v2情况下才有效,但是和彼此冲突。v1v2v1v2

\n

话虽如此,我不能肯定地说允许这个程序是否可以,因为毕竟它不会创建任何实际的可变别名。我绝对没有受过 Miri 检查的 Stacked Borrows 模型的理论基础的教育。

\n