我正在实施一个SPI驱动程序.

现在我有以下驱动程序代码(MRE化版):

use tokio::{join, sync::mpsc};

async fn spi_transmit(write_buf: &[u32], read_buf: &mut [u32]) {
    assert_eq!(read_buf.len(), write_buf.len());
    let (write_fifo, mut read_fifo) = mpsc::channel(2);

    let write_task = async {
        // Simulate an SPI bus that respondes with the sent data + 20,
        // just for demo purposes
        for val in write_buf {
            write_fifo.send(*val + 20).await.unwrap();
        }
    };

    let read_task = async {
        for val in read_buf {
            *val = read_fifo.recv().await.unwrap();
        }
    };

    join!(write_task, read_task);
}

#[tokio::main]
async fn main() {
    let buf_out = [1, 2, 3, 4];
    let mut buf_in = [0, 0, 0, 0];

    spi_transmit(&buf_out, &mut buf_in).await;

    println!("{:?}", buf_in);
}
[21, 22, 23, 24]

中心空气污染指数为async fn spi_transmit(write_buf: &[u32], read_buf: &mut [u32]).

现在我需要实现一个async fn spi_transmit_in_place(read_write_buf: &mut [u32]),它从相同的缓冲区发送和接收数据,该缓冲区最终包含读取的数据.

我知道,因为对缓冲区的读写访问永远不会重叠,所以总是首先读取一个字节,然后在稍后的非重叠时间写入它.

有了这些知识,如果我这样实现它,是否被认为是合理的,或者这是否被认为是未定义的行为?

async fn spi_transmit_in_place(read_write_buf: &mut [u32]) {
    let write_buf = unsafe {
        let data = read_write_buf.as_ptr();
        let len = read_write_buf.len();
        std::slice::from_raw_parts(data, len)
    };

    spi_transmit(write_buf, read_write_buf).await
}

#[tokio::main]
async fn main() {
    let mut buf = [1, 2, 3, 4];
    spi_transmit_in_place(&mut buf).await;
    println!("{:?}", buf);
}
[21, 22, 23, 24]

它是有效的,但是miri is not happy.

如果这是未定义的行为,这是否意味着我必须使用原始指针重新实现spi_transmit?或者我还能怎么解决这个问题呢?

推荐答案

是的,关于同时拥有引用和独占引用Behavior considered undefined告诉我们:

突破pointer aliasing rules大关

如果两个引用都是活动的,则为UB

这些范围不得与LLVM提供的机制分配的任何地址范围重叠

每次将引用或框传递到函数或从函数返回时,都将其视为活动的.

creating them 和 then sequentially accessing them one after the other also makes them live from creation to access 和 thus you don't even need to call a function to make it UB.

Rust相关问答推荐

为什么我们不能通过指针算法将Rust原始指针指向任意地址?'

Rust kill std::processs::child

Rust:跨多个线程使用hashmap Arc和rwlock

将大小为零的类型实例存储到空指针中

将数组转换为HashMap的更简单方法

告诉Rust编译器返回值不包含构造函数中提供的引用

类型批注需要静态生存期

`Pin`有没有不涉及不安全代码的目的?

Rust中WPARAM和VIRTUAL_KEY的比较

如何将实现多个特征的 struct 传递给接受这些特征为&;mut?

是否可以使用Serde/Rust全局处理无效的JSON值?

如何对一个特征的两个实现进行单元测试?

Rust:为什么 &str 不使用 Into

更新 rust ndarray 中矩阵的一行

在构建器模式中捕获 &str 时如何使用生命周期?

为什么在 rust 中删除 vec 之前应该删除元素

意外的正则表达式模式匹配

当 `T` 没有实现 `Debug` 时替代 `unwrap()`

`if let` 只是另一种编写其他 `if` 语句的方式吗?

Rust:为什么在 struct 中borrow 引用会borrow 整个 struct?