rust倒地了怎么起来_如何快速优雅的掌握Rust(四)

本文探讨了Rust编程语言的一些关键概念,包括如何将Rust视为更好的C++选择,Trait与Interface的区别,Attribute与Annotation的不同,泛型与C++ Template的相似与不同,以及PhantomData的作用。文章强调了Rust的静态类型系统和编译时类型确定,以及这些特性对代码安全性和编译速度的影响。
摘要由CSDN通过智能技术生成

1、理解Rust要真正的理解它是一个更好的C++入手

几个必须要搞清楚的高级概念,Rust的Trait和Golang的interface看起来如此相似,到底有什么不同?Rust的属性编程attribute和Java的注解编程有什么不同? 为什么需要phantomdata? 泛型是怎么一回事,和C++的template有什么区别,go又为什么没有?

看起来这些问题不应该是入坑rust 7天的人需要考虑的,并且每个问题似乎都可以展开来写好长一篇。触发我思考这些问题,主要是之前我在一个知乎问题回答里,当时说过Rust也实现了注解编程,马上有人提出反对,这和Java的注解编程完全是两个概念,不能混为一谈。我楞了半天,从形式上看,attribute和annotation真的是一回事,trait和interface也差不多,但确实在实现机制和原理上不一样,所以那位网友反对我的说法,应该是从实现上来说的,他也对,只是我的说法其实也并没有错。

根本的差异在于Rust追求的是编译时类型确定的静态语言,因此它抛弃了大量语言对于反射(go的interface,c++ template,java ioc等)的使用。

2、trait和interface形似神不似

所以第一个要拿出来讨论的就是trait和interface了,前面已经说了,从用户(开发者)的使用体验来看,trait和interface是基本类似的,由于go采用了反射的机制,可以在运行时runtime动态来确定对象的实际类型,所以在代码上显得更简洁优雅elegant:

type Duck interface {
Voice()
}

任何实现了Duck接口的对象,就可以做到“只要它走起来像鸭子,叫起来也像鸭子,他就是一个鸭子”的效果:

func (ducklike *Something) Voice{
...
}

相比而言,Rust的trait需要更多的代码:

impl Duck for Someting {
fn voice(&self) {...}
}

核心不同在于实现机制:Rust是通过编译器在编译的时候就完全推断出(infere)所有的类型,一个对象是否拥有一个方法method都在编译的时候确定,不在运行时通过反射来推断,一方面是让问题提早暴露,另一方面也导致rust的编译速度要比相同大小的go慢的多。

3、atrribute和annotation形似神不似

让我们来看一段代码:

#[derive(Debug, PartialEq)]
pub enum LYSuit {
Nosuit = 0, Clubs = 1, Diamonds = 2, Hearts = 3, Spades = 4
}

#[derive]这一行就给LYSuit这个enum提供了Debug, PartialEq的代码实现,本质上它是用macro展开的形式,在代码里展开成:

impl Debug for LYSuit {
...
}

而看Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {
}

Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。所以这里java的注解是要实现某些特定功能(这一点和rust attribute并无二致),并将其注入到字节码中,这就和attribute相距甚远了。

4、泛型generic和c++ template

可以说二者几乎是一样的,除了Rust提供了一个泛型的绑定概念bound,用于在申明对象时,可以要求模板参数类型必须具备什么样的特性,以减少莫名的错误。拿 https://www.bookstack.cn/read/cpp-to-rust-book/features_of_rust-generics.md 这篇文章的例子来说明:

  1. class PatientRecord {
  2. std::string name_;
  3. PatientRecord() {}
  4. PatientRecord operator= (const PatientRecord &other) { return *this; }
  5. public:
  6. PatientRecord(const std::string &name) : name_(name) {
  7. }
  8. };
  9. ...
  10. std::vector<PatientRecord> records

这里在line 5将方法 operator=申明成了私有,但这个PatientRecord用于申明Vector没有任何问题:records.push_back(PatientRecord("John Doe"));但如果调用records.erase(records.begin());会报错,说PatientRecord PatientRecord::operator=(const PatientRecord&)'is private,所以问题来了,如果在最后调用某个方法才报错误,为什么不能再变量申明的时候就有一种机制能拒绝呢?

Rust就通过泛型的bound机制,要求作为模板参数的类型必须实现某个trait。具体的bound机制可以搜索加以理解,这里就不展开了。

写作本文的时候golang没有模板,实现这种功能,可以通过接口interface达到一定程度的模拟和近似。使用接口,必须自己编写方法,使用几种方法定义命名类型来反转切片是很尴尬的。编写的方法对于每种切片类型都是完全相同的,所以从某种意义上说,我们只是移动并压缩了重复的代码,我们还没有消除它。虽然接口是泛型的一种形式,但它们并没有向我们提供我们想要的所有泛型。https://www.oschina.net/news/108697/why-golang-generic。有意思的是,golang社区正在认真的考虑添加泛型这一重要功能。

5、为什么需要phantomdata

偷个懒,phatomdata在https://www.jianshu.com/p/8554bbf13a02 这篇文章里解释的很清楚。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值