我在测试一段代码:
use std::ptr;
pub unsafe fn from_buf_raw<T: Sized>(ptr: *const T, cap: usize) -> Vec<T> {
let mut dst = Vec::with_capacity(cap);
ptr::copy_nonoverlapping(ptr, dst.as_mut_ptr(), cap);
dst.set_len(cap);
dst
}
#[derive(PartialEq, Debug)]
struct Foo {
data: String,
}
fn main() {
let a = [
Foo {
data: String::from("a"),
},
Foo {
data: String::from("b"),
},
Foo {
data: String::from("c"),
},
];
let v = unsafe { from_buf_raw(&a[1], 2) };
println!("{:?}", v);
assert_eq!(
v,
vec![
Foo {
data: String::from("b")
},
Foo {
data: String::from("c")
},
]
);
}
playground:
- https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=cf3f580196ab31820703c6000c39577a
是可以正常输出的,但是改成测试:
use std::ptr;
pub unsafe fn from_buf_raw<T: Sized>(ptr: *const T, cap: usize) -> Vec<T> {
let mut dst = Vec::with_capacity(cap);
ptr::copy_nonoverlapping(ptr, dst.as_mut_ptr(), cap);
dst.set_len(cap);
dst
}
#[derive(PartialEq, Debug)]
struct Foo {
data: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_no_copy_trait() {
#[derive(PartialEq, Debug)]
struct Foo {
data: String,
}
let a = [
Foo { data: String::from("a") },
Foo { data: String::from("b") },
Foo { data: String::from("c") },
];
let v = unsafe { from_buf_raw(&a[1], 2) };
println!("{:?}", v);
assert_eq!(v, vec![Foo { data: String::from("b") },
Foo { data: String::from("c") }, ]);
}
}
就报错:
free(): double free detected in tcache 2
error: test failed, to rerun pass `--lib`
Caused by:
process didn't exit successfully
这里的 v 不是 copy 来的吗?为什么会报错 free 了两次呢?
1
共 9 条评论, 1 页
评论区
写评论学习了学习了; 这里实际的类型可能不是String,而是一个范型。 我在研究如何复制任意包含一个范型类型的Vector,结果发现不行。
--
👇
Pikachu: 最好的办法是给T加上T: Copy的约束。
copy_nonoverlapping的文档都写了,对非Copy类型使用这个函数可能导致内存问题。
我的建议是,写unsafe代码时,确保以下几点
如果没满足这些条件的话,最好还是想办法用safe rust实现。比如说,我就不太理解你这里为什么不直接写a[1..].to_vec()。
最好的办法是给T加上T: Copy的约束。
copy_nonoverlapping的文档都写了,对非Copy类型使用这个函数可能导致内存问题。
我的建议是,写unsafe代码时,确保以下几点
如果没满足这些条件的话,最好还是想办法用safe rust实现。比如说,我就不太理解你这里为什么不直接写a[1..].to_vec()。
懂了,就是Copy的整个区间,我要用forget标注一下。
--
👇
Grobycn: 你用了
unsafe
, 就要靠自己保证安全。--
👇
JasonkayZK: 嗯,我也怀疑是因为只是复制了 String 底层的指针导致的;
那我要怎么通过 copy_nonoverlapping 来完整复制这个String呢?貌似不行。
那如果某些结构存在这种底层引用的指针,是不是就不能使用 copy_nonoverlapping 了?
--
👇
Grobycn: 明白
String
的内存布局是怎么样的,你就知道为什么会 double free.String
和Vec
差不多,大概是这样的:它们
drop
的时候,会负责回收data
指向的内存。而你的复制只是复制了data
这个指针,而没有复制data
指向的数据。你用了
unsafe
, 就要靠自己保证安全。--
👇
JasonkayZK: 嗯,我也怀疑是因为只是复制了 String 底层的指针导致的;
那我要怎么通过 copy_nonoverlapping 来完整复制这个String呢?貌似不行。
那如果某些结构存在这种底层引用的指针,是不是就不能使用 copy_nonoverlapping 了?
--
👇
Grobycn: 明白
String
的内存布局是怎么样的,你就知道为什么会 double free.String
和Vec
差不多,大概是这样的:它们
drop
的时候,会负责回收data
指向的内存。而你的复制只是复制了data
这个指针,而没有复制data
指向的数据。嗯,我也怀疑是因为只是复制了 String 底层的指针导致的;
那我要怎么通过 copy_nonoverlapping 来完整复制这个String呢?貌似不行。
那如果某些结构存在这种底层引用的指针,是不是就不能使用 copy_nonoverlapping 了?
--
👇
Grobycn: 明白
String
的内存布局是怎么样的,你就知道为什么会 double free.String
和Vec
差不多,大概是这样的:它们
drop
的时候,会负责回收data
指向的内存。而你的复制只是复制了data
这个指针,而没有复制data
指向的数据。明白
String
的内存布局是怎么样的,你就知道为什么会 double free.String
和Vec
差不多,大概是这样的:它们
drop
的时候,会负责回收data
指向的内存。而你的复制只是复制了data
这个指针,而没有复制data
指向的数据。感觉是不是被优化了
--
👇
JasonkayZK: 最下面加了
mem::forget(v);
之后,避免 v 释放内存后可以执行了。试了一下,貌似异常退出了 Finished dev [unoptimized + debuginfo] target(s) in 0.02s Running
target\debug\ol.exe
[Foo { data: "b" }, Foo { data: "c" }] error: process didn't exit successfully:target\debug\ol.exe
(exit code: 0xc0000374, STATUS_HEAP_CORRUPTION)最下面加了
mem::forget(v);
之后,避免 v 释放内存后可以执行了。