当前位置: 首页 > 科技观察

Rust语言:类型转换,你还可以这样玩,你学会了吗

时间:2023-03-19 22:19:02 科技观察

你有没有遇到过一个设计奇葩的函数的形参,但是你手里却没有合适的变量类型?你有没有绞尽脑汁想在各种嵌套类型中提取对核心变量的引用呢?只要了解了Rust中几种常见的类型转换,就可以彻底告别编译器的红色警告,享受写完就编译通过的乐趣。什么是类型转换?类型转换就是在调用函数时,根据函数要求的参数签名类型A,将我们手中的类型B转换为类型A的过程,并且B的归属是不能改变的。一般的函数调用需要我们传递引用,很少需要直接传递所有权。很多语言都提供了向上转型和向下转型,比如Java和C++,所以使用(B)A可以强制B到A。但是强制转型有一定的风险。在Java中强制转换失败会抛出CastException,但在C++中有时不会抛出异常。必须使用高版本的cast系列函数进行转换,以保证在转换失败时,给出cast异常。避免内存安全问题的异常。尽管Rust是一门安全风险可控的编程语言,但有些事情还是需要开发者提前了解的。例如,整数类型之间强制转换的结果并不令人满意。asoperator1.在编码过程中,使用最多的转换是整数的强制转换。我们经常会遇到这样的类型需求usize。这种类型一般是指长度或者数组索引。我们只有i32这样的整型变量不能透传进来,必须使用as关键字进行强制转换,才能通过Compiler检测。然而,这里发生了一些意想不到的事情,比如类型截断。什么是类型截断,即将一个取值范围较大的变量A转换为取值范围较小的变量B,如果超出范围,则A减去B的区间长度。比如128超出了i8类型的范围(-128,127),强制转换后的值等于128-256=-128。2.在学习Traits的时候,我们发现了一个问题。TypeA可以实现很多Traits,有些Traits存在函数签名相同,只是内部实现不同。如果以A作为主题来调用,编译器无法判断应该调用哪个函数,所以必须将A强制向上到一个具体的Trait来告诉编译器如何判断。比如B和C是同名的trait,函数name(),A分别实现了它们。当A要调用B的name()时,需要显示转??换以避免歧义,如::name()。3.as也可以在父类型和子类型之间进行转换,比如&'staticstr和&'astr。'静态生命周期在整个进程生命周期内都有效,'a的生命周期更短。我们称&'static为&'a的子类型,用'static:'a来表示。as可以自由转换父子类型,比如&'staticstras&'astr,这种做法的意义是为了满足一些函数对于生命周期的要求。From和Into这两个特征在标准库的转换模块中定义。事实上,它们做的是同一件事,所以不要被From和Into搞混了。打个形象的比喻,我吃,我吃一样的东西。只要我在吃,饭就一定是我在吃,这是一个道理。Rust还为此定义了一个定理:如果类型A实现了From,那么可以通过调用into方法将类型B的实例转换为类型A。比如我们常见的字符串String类型实现了From(&str),那么&str就可以通过into()转化为String。在大多数情况下,我们只需要实现FromTrait,Rust会自动为所有From实现反向实现Into。From和Into并非完全没有例外。当我们不确定转换的结果是否是我们想要的时,我们可以实现TryFrom和TryInto这两个Traits来捕获可能的错误信息。AsRef和A??sMutAsRef和A??sMut可以分别将类型转换为不可变引用和可变引用。这两个特性对我们实现可扩展的功能很有帮助。比如我们要设计一个同时允许&String和&str传入的函数,那么像下图的测试函数那样做:如果我们自己定义一个类型,最好实现AsRefTrait,这样会给我们带来很多意想不到的好处。FromStrRust有很多内置的实现来帮助我们进行类型转换。字符串与其他类型的转换默认会实现FromStrTrait。如果要将字符串转换为整数,可以使用parse函数,它会自动根据返回值类型解析字符串,得到正确类型的返回值。整型转字符串就不用说了,格式化宏直接格式化就可以了。最后,虽然Rust的类型很多,但是类型的统一风格还是不错的。通过各种Traits,可以统一类型之间的逻辑上下文。不得不说,理解了类型转换之后,在设计代码的时候,可以更优雅,更兼容。作为一个十几年的程序员,语言之间是有共性的。放眼所有的编程语言,Rust在类型设计上就没有那么优雅了,尤其是在结合了泛型和生命周期之后,让程序设计者对代码的直觉理解降低到了一个恐怖的程度。既然选择学习Rust,就一定要有相关意识,陡峭的学习曲线更能拉开与懒人的差距。学会努力工作比努力工作本身更重要。