use std::{marker::PhantomPinned, pin::Pin, ptr::NonNull};
pub struct User {
pub name: String,
}
pub struct Token<'a> {
pub data: &'a str,
}
impl<'a> Token<'a> {
pub fn data(&self) -> String {
self.data.to_string()
}
}
pub fn user_token<'a>(user: &'a User) -> Token<'a> {
Token { data: &user.name }
}
pub struct ResData<'a> {
pub user: User,
pub token: Token<'a>,
_pin: PhantomPinned,
}
// 假设 User, Token, user_token 定义来自第三方库, 需要返回token信息
pub fn build<'a>() -> Pin<Box<ResData<'a>>> {
let user = User {
name: "hello".to_string(),
};
let ptr = NonNull::from(&user);
let token = user_token(unsafe { ptr.as_ref() });
let data = ResData {
user,
token,
_pin: PhantomPinned,
};
Box::pin(data)
}
#[test]
fn test() {
let data = build();
assert_eq!(data.token.data(), "hello")
}
1
共 5 条评论, 1 页
评论区
写评论data是分配到栈上的,在一般的情况下,这时候的“自引用”已经被赋值给栈上的引用,然后data才被移动pin到堆上,会出现指针指向错误。所以一般需要先pin再赋值。
在本例不出问题的原因是,String本身字符串是放在堆上的,&str切片直接切的是堆上的字符串,并没有引用到ResData结构体自身,如果改成&String引用,那么也会出现一般情况下的指针指向错误。修正就是先pin再取指针赋值
--
👇
zylthinking: 请教一下:
这里的 data 不能保证 data, data.user.name 分配在什么地方吧, 虽然大概率是在 heap 上; Box::pin(data) 语义其实应该将 data 放在在 heap 上, 但因为 data 本身不保证放在什么地方, 因此, user 也就不能保证在什么地方, 虽然 data.user.name.as_str() 作为一个潜在可被改写, 扩充的缓冲区一般在 heap 上, 但这也是凑巧...
因此, 将自引用的 ResData 通过 Box::pin 返回去, 会不会在一般情况下(非本例)导致内存非法引用呢
字符串切片类型是一个比较特殊的类型,所以说你写的严格来说某种程度上不是自引用。因为String的实际字符串是存在堆上的,String创建出来的&str引用切片,都会是直接指向堆上切片的胖指针,而不是直接指向String结构体的,所以你不需要pin也是没问题的。 如果是其他的情况,一般是先把非自引用的部分,先pin到堆上,确保其不再移动后,再赋值自引用的部分。 按照你原始代码举例,如果token的内部类型改为&String,那么就会出现指针指向错误,因为你先把栈上的&String赋值了,然后又把String移到了堆上(pin) 第一位老哥给了一个教程网站,里面讲的就挺好的,可以看看
输出: 0x7ffed25d39a8 0x7ffed25d39a8 0x6322c61a9b80 0x6322c61a9ae0 0x6322c61a9ae0 0x6322c61a9b80
只有 data.user.name.as_str() 相同, 显示出确实是利用了 String 内部持有的是一个 pointer 这个事实才没有导致未定义行为, 否则, 自引用必然被破坏
请教一下:
这里的 data 不能保证 data, data.user.name 分配在什么地方吧, 虽然大概率是在 heap 上; Box::pin(data) 语义其实应该将 data 放在在 heap 上, 但因为 data 本身不保证放在什么地方, 因此, user 也就不能保证在什么地方, 虽然 data.user.name.as_str() 作为一个潜在可被改写, 扩充的缓冲区一般在 heap 上, 但这也是凑巧...
因此, 将自引用的 ResData 通过 Box::pin 返回去, 会不会在一般情况下(非本例)导致内存非法引用呢
你是不是在找: https://course.rs/advance/circle-self-ref/self-referential.html