rust - 暂时放弃 Rust 中可变借用内容的所有权是否安全?
问题描述
一个通过函数修改&mut T
原位的函数FnOnce(T) -> T
是否可以安全地生锈,还是会导致未定义的行为?它是包含在某个地方的标准库中,还是包含在一个知名的 crate 中?
如果您还假设T: Default
,那看起来像
fn modify<T, F: FnOnce(T) -> T>(x: &mut T, f: F) -> ()
where
T: Default
{
let val = std::mem::take(x);
let val = f(val);
*x = val;
}
我的问题是关于做同样的事情但放弃where T: Default
条款(也没有T: Clone
)。这需要不同的实现,因为您不能使用std::mem::take
. 我不确定如何实现不受约束的版本,但应该可以使用不安全的 Rust。
我正在从线性类型和子结构逻辑的背景中学习 Rust。Rust 的可变借用似乎与将资源移入然后移出函数非常相似,但我不知道像这样临时拥有可变借用的内容是否真的安全。
解决方案
它是安全的,甚至还有板条箱(现在找不到)。
然而。
在编写不安全的代码时,您必须非常小心。如果你不知道自己在做什么,很容易导致UB。
例如,这里有一些你可能没有想到的东西:恐慌安全。
假设我们简单地实现它:
pub fn modify<T, F: FnOnce(T) -> T>(v: &mut T, f: F) {
let prev = unsafe { std::ptr::read(v) };
let new = f(prev);
unsafe { std::ptr::write(v, new) };
}
微不足道的对。
或者是吗?
fn main() {
struct MyStruct(pub i32);
impl Drop for MyStruct {
fn drop(&mut self) {
println!("MyStruct({}) dropped", self.0);
}
}
let mut v = MyStruct(123);
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
modify(&mut v, |_prev| {
// `prev` is dropped here.
panic!("Haha, evil panic!");
})
}))
.unwrap_err();
v.0 = 456; // Writing to an uninitialized memory!
// `v` is dropped here, double drop!
}
我使用了一个自定义类型,它的析构函数除了打印之外什么都不做,但是想象一下如果这是一个Vec
释放内存并且我们正在写入释放的内存(然后,作为奖励,获得双重释放)会发生什么。
就像@Kendas 所说的那样,当没有中断点时,在 Rust 中将内存保持在未初始化状态是有效的,这是正确的。问题是,实际上中断点比您希望的要多得多。事实上,在编写不安全的代码时,您必须考虑对外部代码的任何调用(即不是您的代码,也不是您相信不会做坏事的代码,例如 std)是一个中断点。
不安全的代码很难。最好留在安全的土地上。
编辑:您可能想知道它AssertUnwindSafe
是什么。也许您甚至尝试删除它并注意到它没有编译。嗯,UnwindSafe
这是一种保护,AssertUnwindSafe
是一种绕过保护的方法。
你可能会问,这有什么意义?关键是,这种保护确实不准确。如此不准确,绕过它甚至不需要不安全。但它仍然存在,所以我们意外UB的可能性较低。
作为 API 的编写者,这对你来说并不重要——你应该表现得好像这种保护不存在,因为绕过它是安全的,而且很容易被错误地绕过。Rust 标准库本身在过去也有类似的错误(#86443,#81740,... - 它们都在同一个代码中并非偶然 - 这些问题往往以块的形式出现。但是有更多的)。
推荐阅读
- html - Bootsrap 固定导航栏没有边距底部
- vuejs2 - 如何清空 Vuex 存储模块状态对象?
- flutter - onBackgroundMessage 打开指定页面
- java - 如何在Java中将字符串表示为二叉树?
- visual-studio-code - 如何使用gnu格式样式格式化vscode中的c代码?
- r - R中的CompareGroup函数_组数大于5时如何编写r代码
- wordpress - WordPress - 使用参数设置自定义帖子类型默认类别(分类术语)
- jq - How to use jq to not show a json data if a field is an empty array?
- javascript - 如何摆脱 Firebase 自动生成的 ID
- node.js - 如何从 json 格式的响应中获取特定字段