作者回复: 这里我写的不太严谨。强类型和弱类型的定义一直不太明确,wikipedia 上也没有一个标准的说法。https://en.wikipedia.org/wiki/Strong_and_weak_typing。我一般是看类型在调用时是否会发生隐式转换,所以说 python 是弱类型。不过 wikipedia 在介绍 python 时确实说它是 strongly typed:https://en.wikipedia.org/wiki/Python_(programming_language)。 但如果按照类型是否会隐式转换,Rust 是强类型,Python 和 C 是弱类型: ```rust fn main() { let a = 42u8; let b = 42.0f64; // 不会做隐式转换 println!("a+b = {}", a+b); } ``` Python 会做类型的隐式转换: ```python def add_numbers(a, b): return a + b if __name__ == '__main__': a = 42 b = 42.0 print(add_numbers(a,b)) ``` C 也会: ```C #include <stdio.h> int add_numbers(int a, int b) { int result = a + b; return result; } int main() { char c = "42"; int n = 42; // 为什么说 C 是 weakly typed int result = add_numbers(c, n); printf("%d\n", result); return 0; } ```
作者回复: 虚表相当于在运行时生成的一个涵盖了一系列函数指针的数据结构。有时候对于不同类型但是满足相同接口的数据,我们希望可以抹去他们的原始类型,让它们有相同的接口类型,以便于统一处理,这样更加灵活,但此时需要为每个数据构造他们各自对接口实现的虚表,这样可以依旧调用到属于该类型的实现。 虚表一般存储在堆上。Rust 下也有虚表的栈实现:https://github.com/archshift/dynstack
作者回复: 正确!非常棒!
作者回复: 非常棒!
作者回复: 好问题。这个在讲 trait 的那一课有讲到。虚表是每个 impl TraitA for TypeB {} 时就会编译出一份。比如 String 的 Debug 实现, String 的 Display 实现各有一份虚表,它们在编译时就生成并放在了二进制文件中(大多是 RODATA 段中)。 所以虚表是每个 (Trait, Type) 一份。并且在编译时就生成好了。 如果你感兴趣,可以在 playground 里运行这段代码(这是后面讲 trait 时使用的代码):https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=89311eb50772982723a39b23874b20d6。限于篇幅,代码就不贴了。
作者回复: 嗯,前两篇都是每个程序员最好掌握的基础知识。
作者回复: 虚表你可以理解成一张指向若干个函数地址的表。这样在运行时,可以通过这张表找出要执行的函数,进而执行。比如 w.write(),如果 w 是一个类型被抹去的引用,指向了一块地址,此时 w 如何能够执行到 write() 方法?只能通过运行时构造的虚表来解决。
作者回复: 1 的题意可以看这个代码: ```C #include "stdio.h" void hello() { printf("Hello world!\n"); } int main() { char buf[1024]; void (* p)() = &hello; (*p)(); int *p1 = (int *) p; p1[1] = 0xdeadbeef; } ``` 在 C 语言中,这种操作是允许的,但会造成内存访问越界,导致崩溃;在 Rust 中,编译器会拒绝这样的操作。 2/3 正确!
作者回复: 对是 error lens。感谢 coer 的解答。https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens
作者回复: 嗯。限于篇幅,确实有好多东西无法都讲到。状态机在讲 async/await 的时候会具体介绍。 单态化在具体讲泛型时会介绍。 虚表会在讲 trait 时具体介绍。 动态分发的性能比静态分发差很多,可能是数量级的差别。静态分发单态化后直接跳转到方法执行;动态分发会先找到虚表,再找到虚表中对应的方法,再跳转到对应的方法执行。这个过程会大概率有两次 cache miss。 代码倒并不太会不好理解吧,尤其是现在不加 dyn 会有编译器 warning,dyn T 还是一目了然知道是哪个 trait 的 trait object。