gpt4 book ai didi

rust - Rust:使用函数修改引用的引用;它包含UB吗?

转载 作者:行者123 更新时间:2023-12-03 11:30:33 26 4
gpt4 key购买 nike

最近,我写了以下内容:

use std::ptr;

fn modify_mut_ret<T,R,F> (ptr: &mut T, f: F) -> R
where F: FnOnce(T) -> (T,R)
{
unsafe {
let (t,r) = f(ptr::read(ptr));
ptr::write(ptr,t);
r
}
}
这是一个简单的实用程序,因此我希望它在标准库中,但找不到(至少在 std::mem中)。例如,如果我们假设使用 T: Default,则可以安全地实现此操作,而需要额外的 drop开销:
use std::mem;

#[inline]
fn modify_mut_ret<T,R,F>(ptr: &mut T, f: F) -> R
where F: FnOnce(T) -> (T,R),
T: Default
{
let mut t = T::default();
mem::swap(ptr, &mut t);
let (t,r) = f(t);
*ptr = t;
r
}
我不认为第一个实现包含任何未定义的行为:我们没有对齐问题,并且我们使用 ptr::write消除了与 ptr::read复制的两个所有权之一。但是我担心 std似乎不包含具有此行为的函数这一事实。我有什么错还是忘记了什么?上面的不安全代码是否包含任何UB?

最佳答案

此代码仅包含UB的一个实例,这是因为该函数可以提前返回。让我们仔细看一下(我移动了一些东西以使其更容易分解):

fn modify_mut_ret<T, R, F: FnOnce(T) -> (T, R)>(x: &mut T, f: F) -> R {
unsafe {
let old_val = ptr::read(x); // Copied from original value, two copies of the
// same non-Copy object exist now
let (t, r) = f(old_val); // Supplied one copy to the closure
ptr::write(x, t); // Erased the second copy by writing without dropping it
r
}
}
如果闭包运行正常,则外部函数将正常进行,并且 x的旧值的副本总数将仅保留一个副本,该副本将由闭包拥有,可能会存储也可能不会存储稍后在 Rc<RefCell<...>>/ Arc<RwLock<...>>或全局变量中。
但是,如果它感到 panic ,并且使用 modify_mut_ret调用 std::panic::catch_unwind的代码捕获了 panic ,则将存在 x的旧值的两个副本,因为尚未达到 ptr::write,但已经达到 ptr::read
您需要做的是通过中止进程来应对 panic :
use std::{ptr, panic::{catch_unwind, AssertUnwindSafe}};

fn modify_mut_ret<T, R, F>(x: &mut T, f: F) -> R
where F: FnOnce(T) -> (T, R) {
unsafe {
let old_val = ptr::read(x);
let (t, r) = catch_unwind(AssertUnwindSafe(|| f(old_val)))
.unwrap_or_else(|_| std::process::abort());
ptr::write(x, t); // Erased the second copy by writing without dropping it
r
}
}
这样,在闭包中 panic 将永远不会离开该函数,因为它将在任何其他代码观察到重复的值之前捕捉到 panic 并立即中止该过程。
出现 AssertUnwindSafe是因为我们必须确保不会观察到由于 panic 而创建的逻辑上无效的值,因为我们总是会在 panic 之后中止。有关更多信息,请参见 UnwindSafe 's documentation

关于rust - Rust:使用函数修改引用的引用;它包含UB吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65284043/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com