晚上闲着睡不着,继续看rust,正好看到rust Box这块,对实现很有兴趣,想研究一下是怎样实现的,结果搞了1小时多才解决,因为知识的盲区,做了好多无用功。把相关的资料和研究的过程发一遍,避免后续有人遇到相同的问题。
1问题的由来
看rust圣经,感觉Box实现的很巧妙,既然巧妙,一定要好好学习一下他的思路,直接扒开源码查看
1 2 3 4 5 6 7 | pub struct Box<
T: ?Sized,
>(Unique<T>, A);
|
嗯,确实很巧妙,通过一个元组结构体来实现,顺便看看 Box::new()是怎么实现的。结果一看把人看蒙了。为什么标准库会有死循环这么大的漏洞,不对呀,我刚才明明还在用啊。
1 2 3 4 5 6 7 8 | pub fn new(x: T) - > Self {
Box::new(x)
}
|
2初步解决问题
当时的疑问就是,为什么这个函数不会死循环?
那肯定难不倒我啊,直接动态调试,查看反汇编
为什么我看不到调用Box的impl啊,好吧,既然动态没有什么有效信息,拉到ida去看看,虽然没什么大用。看看也是好的。
可能是ida版本有点低,对rust的识别还是有点问题的,修复了一下,还真发现了一点,Box::new好像是被编译器优化或是替换了。
1 | int * __fastcall alloc::boxed::impl_0::new( int )
|
感觉就是编译器在编译的时候,将返回值Box::new(x) 替换成了 alloc::alloc::exchange_malloc(),但是不是不得骑姐,大概是猜到不会死循环的原理是因为编译器在编译过程做了函数替换,但是编译器怎么替换的,一点思路都没有。
3最终解决
其实到这一步,已经进入了知识盲区,如果不补充相关知识是真的没法继续想明白了,
因为这一步其实涉及到的编译器的相关知识了。
还记得我提到过的Box::new()源码中的定义的那么多宏吗, 嗯,没错,就是他们搞的鬼,因为编译器在编译过程中会识别这些宏,Box::new()会在编译器规则下转换为exchange_malloc(),嗯,不是我胡说,rust-lang 仓库源码明明白白写的。
1 2 3 4 5 6 | let exchange_malloc = Operand::function_handle(
tcx,
tcx.require_lang_item(LangItem::ExchangeMalloc, Some(expr_span)),
ty:: List ::empty(),
expr_span,
);
|
至于代码是什么意思,和本篇关系不是很大了,有兴趣的可以自己了解一下相关只是了,嗯,到此为止,疑惑解决。
当然,这个不是我脑瓜一拍就想到的,都说了是知识盲区,我也是找了资料才知道的,看完以后恍然大悟,如果早点去伸手,岂不是能省下这一个小时做其他事?,不过也学到了很多,算是事倍功半吧。
相关链接:https://stackoverflow.com/questions/73903346/how-box-smart-pointer-is-implemented
补充:有一说一, rust好用归好用,就是使用起来条框太多了, 现在一写rust 满脑子都是unsafe
最后说一点,我也是刚刚学习rust,本篇文章的观点和认知可能有问题,如果出错的话,请大佬指出,这边先说一声谢谢了
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2023-2-2 01:43
被青丝梦编辑
,原因: 补充