< 返回我的博客

爱国的张浩予 发表于 2021-02-25 17:00

Tags:dynamic,dispatch,pointer

两张图讲透Rust仅有的类动态分派指针

trait Object胖指针

输入图片说明

切片胖指针

输入图片说明

与普通指针相比的特别之处

内存占用的体量不同

相较于usize大小的普通指针,指针(i.e. Wide References)同时占用了usize宽度。此特点在上两张图中已非常具象化了。

内存寻址方式不同

普通指针是先寻址到‘变量值’,再根据变量值中引用的‘类型信息’,确定

  • 待调用‘成员方法’的内存位置,或
  • ‘数组’的长度 — 数据长度是数组类型定义的一部分,是完全静态的属性值,不可在运行时被动态修改。

所以,普通指针对被引用值的数据结构一无所知,但变量值自备了它的‘类型信息’。

另一方面,指针对被引用值的数据结构有部分了解,所以无须变量值自带‘类型信息’,并能绕过‘类型信息’

  • 直接调用trait Object上的成员方法。览阅由胖指针内第二个usize值引用的vtable,每个trait method实现函数的内存偏移量一目了然。
  • 直接对切片做边界检查、索引计算、甚至读取全部数据,因为切片长度就保存于胖指针内的第二个usize值里。

就效果而言,借助指针,‘变量值’与‘类型信息’之间的强关联关系妥善被解耦了。这就是“Rust能够模仿OOP语言,从多个互不相容的类型定义中,归纳出共有interface契约”的技术基石:

  1. trait Trait作为对若干相似类型定义struct Type中共有trait method集合的统一描述 — 这就不需要知道每个类的具体‘类型信息’了。
  2. 以每个dyn Trait所独有的vtable为“路引”,寻址被调用trait method的具体实现函数。

完美!

总结

类型 组成部分 元数据含义 动态分派方式
dyn Trait 数据指针 + vtable trait method地址表 运行时vtable,寻址trait method具体实现函数
slice 数据指针 + 长度 元素个数 运行时的边界检查与索引计算

结束语

此次分享的关键是文章最开始的两张图。但对Rust内存寻址方式有所了解,才能提高Rust编程的首次成功率,减少rustc报警的次数。

评论区

写评论

还没有评论

1 共 0 条评论, 1 页