Rust 中的类型转换

1. as 运算符

as 运算符有点像 C 中的强制类型转换,区别在于,它只能用于原始类型(i32i64f32
f64u8u32char 等类型),并且它是安全的

在 Rust 中,不同的数值类型是不能进行隐式转换的,比如:

 let b: i64 = 1i32;

会出现编译错误,提示无法进行类型转换。

error[E0308]: mismatched types
 --> src\main.rs:2:18
    |
2   |     let b: i64 = 1i32;
    |                  ^^^^ expected i64, found i32
help: change the type of the numeric literal from `i32` to `i64`

这时可以使用as 进行转换。

let b: i64 = 1i32 as i64;
  • 为什么它是安全的?

    尝试以下代码:

    let b = 1i32 as char;

    编译器错误:

    error[E0604]: only `u8` can be cast as `char`, not `i32`
    --> src\main.rs:2:13
        |
    2   |     let b = 1i32 as char;
        |             ^^^^^^^^^^^^

    可见在不相关的类型之间,Rust 会拒绝转换,这也避免了运行时错误。

2. Trait From<T>Into<T>

上文说到,as 运算符之能在原始类型之间进行转换,那么对于 Struct 和 Enum 这样的类型该如何进行转换呢? 这就是我们这节的内容 From<T>Into<T>

先来看一看这两个 Trait 的结构。

pub trait From<T> {
    fn from(T) -> Self;
}
pub trait Into<T> {
    fn into(self) -> T;
}

很简单,From<T> 有一个 from 方法,Into<T> 有一个 into 方法。

一般来说,我们应该尽量优先选择实现 From<T> 而不是 Into<T> ,因为当你为 U 实现 From<T> ,这意味着你同时也为 T 隐式实现了 Into<U>

来看个例子

fn main() {
    println!("Hello, world!");
    let b: Complex = 1.into();
    println!("{:?}", b);
}
#[derive(Debug)]
struct Complex {
    re: i32,
    im: i32
}

impl From<i32> for Complex{
    fn from(re: i32) -> Self {
        Complex{
            re,
            im:0
        }
    }
}

当我为 Complex 实现 From<i32> 后,我也可以在 i32 上使用 into 方法,转换到 Complex

原始类型实现了与 as 转换相对应的 From<T>Into<T>

当你为 U 实现 From<T> 之后,你要确保这个转换一定能成功,如若有失败的可能,你应该选择为 U 实现 TryFrom<T>

  • 什么时候该使用 Into<T>

    Into<T> 被设计出来,总有该用到的地方。那什么时候该使用呢?

    先复习一下 Rust 中的 孤儿原则

    在声明trait和impl trait的时候,Rust规定了一个Orphan Rule(孤儿规则):impl块要么与trait的声明在同一个的crate中,要么与类型的声明在同一个crate中。

    也就是说,不能在一个crate中,针对一个外部的类型,实现一个外部的trait。

    因为在其它的crate中,一个类型没有实现一个trait,很可能是有意的设计。

    如果我们在使用其它的crate的时候,强行把它们“拉郎配”,是会制造出bug的。

    比如说,我们写了一个程序,引用了外部库lib1和lib2,lib1中声明了一个trait T,lib2中声明了一个struct S ,我们不能在自己的程序中针对S实现T。

    这也意味着,上游开发者在给别人写库的时候,尤其要注意。

    一些比较常见的标准库中的 trait,比如 Display Debug ToString Default 等,应该尽可能地提供好。

    否则,使用这个库的下游开发者,是没办法帮我们把这些 trait 实现的。

    同理,如果是匿名impl,那么这个impl块必须与类型本身存在于同一个模块中。

    来自 F001 https://zhuanlan.zhihu.com/p/21568827

    显然, From<T> 不属于当前 crate ,当你要实现当前 crate 中的类型 T 转换到其他 crate 中的类型 U 时,如果选择为 U 实现 From<T> ,由于孤儿原则,编译器会阻止你这么做。这时我们就可以选择为 T 实现 Into<U>

    注意,和 From<T> 不同,实现 Into<U> 之后并不会隐式实现 From<T> ,这点需特别注意。

  • From<T> 的妙用

    回忆一下 Rust 的 ? 操作符,它被用于 返回值为 Result<T,E> 或者 Option<T> 的函数。回想一下,它是如何处理 Err(E) 的。

    fn apply() -> Result<i32,i32> {
        Err(1)
    }
    fn main() -> Result<(),i64> {
        let a = apply()?;
        Ok(())
    }

    上面的例子是可以通过编译的,既然 Rust 中的数值类型是不能隐式转换的,那么,当返回 Err(i32) 时是如何转换到 Err(i64) 的呢?这其实是一个 Rust 的语法糖。展开后的代码类似于下面:

    fn apply() -> Result<i32,i32> {
        Err(1)
    }
    fn main() -> Result<(),i64> {
        let a = match apply() {
            Ok(v) => v,
            Err(e) => return Err(i64::from(e)),
        };
        Ok(())
    }

    也就是说,Rust 会自动调用目标类 from 方法进行转换。

3. 解引用强制多态

这次先看一个例子:

fn print(message: &str) {
    println!("{}",message);
}
fn main() {
    let message: String = "message".to_string();
    print(&message);
}

print 的形参是 &str 类型,然而在 main 中,我传递却是一个 &String 类型的实参。明显,这两个类型不相同!!Rust 为什么会通过这样的代码呢?

没错,这就是 Rust 的 解引用强制多态。

首先,需要了解一个 Deref Trait 。

#[lang = "deref"]
pub trait Deref {

    type Target: ?Sized;

    #[must_use]
    fn deref(&self) -> &Self::Target;
}

deref 方法返回一个 &Target 类型的引用。

回忆一下 Rust 中的解引用语法,当 ref 是一个引用或智能指针时,我们可以使用 *ref 的方式解引用。这是类似一个语法糖,对于 *ref 这种写法,写全应该时 *(ref.deref())

回想 Box<T> 的使用,Box<T> 实现了 Deref ,它的 deref 方法返回 &T 的引用,然后使用解引用运算符 * ,我们顺利拿到一个 T 类型的数据。也就是,你可以通过实现 Deref 以重载解引用运算符。

Deref 和这节的内容有什么关系呢?

T 实现了 Deref<Target=U> 时,对于需要 &U 的地方,你可以提供一个 &T 类型的数据,Rust会为你自动调用 deref 方法,而这个过程可以重复多次。

比如,我自定义类型 P 实现了 Deref<Target=String> ,那么可以把 &P 类型变量传递给一个 &str 类型变量。&P -> &String -> &str ,伪代码: &P.deref().deref()

回到这节开头的例子,print(&message) 相当于 print((&message).deref()) ,正好是一个 &str 类型。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Rust 没有类似 Java 的 Object 类型。Rust 的类型系统更加严格,所有类型都必须明确指定。如果需要存储不同类型的值,可以使用枚举类型 (enum) 或类型别名 (type alias)。 ### 回答2: 在Rust,没有直接对应Java的Object类型。Rust是一门静态类型的编程语言,它使用trait(特征)来实现多态性和泛型。在Rust,trait是定义行为的方式,相当于Java的接口。 在Rust,我们可以使用trait来创建具有通用行为的类型。这些类型可以实现trait,从而具有该trait定义的行为。通过使用泛型,我们可以在不同的类型上实现相同的trait。这使得Rust可以实现类似JavaObject类型的功能。 在Rust,任何实现了Sized trait的类型都可以作为trait对象使用。Sized trait是一个标记trait,表示该类型在编译时已知其大小。只有这样的类型才可以在Rust有固定的内存布局。可以使用dyn关键字来创建一个动态分发的trait对象,类似于Java的Object。 例如,我们可以定义一个包含一个方法的Trait,并为不同的类型实现该Trait: ```rust trait MyTrait { fn do_something(&self); } struct MyStruct; impl MyTrait for MyStruct { fn do_something(&self) { println!("Doing something!"); } } fn main() { let obj: &dyn MyTrait = &MyStruct; obj.do_something(); } ``` 在上面的例子,我们定义了一个叫做MyTrait的trait,并为MyStruct实现了该trait。通过使用dyn关键字和&引用,我们可以将MyStruct实例作为trait对象进行操作,并调用do_something方法。 通过使用trait和泛型,Rust可以实现类似JavaObject类型的灵活性和多态性。这使得Rust能够在编译时保持强类型的特性,同时提供类似动态类型语言的灵活性。 ### 回答3: 在Rust,没有直接对应于Java的`Object`类型的内建类型。不过,你可以使用Rust的`dyn Any` trait来实现类似的功能。 `dyn Any` trait是Rust标准库的一个特征(trait),它允许你在运行时对类型进行类型检查和转换。`dyn Any` trait可以用于检查一个值是否为特定类型的实例,以及在类型安全的前提下转换它们的类型。 具体地说,在Rust,你可以通过使用`dyn Any` trait来实现一种类似`Object`类型的通用对象。通过将具体类型包装到`Box<dyn Any>`,你可以将不同类型的值存储在同一个容器,并且在需要时对其进行类型检查和转换。 以下是一个简单的示例,展示了如何使用`dyn Any` trait来实现类似`Object`类型的通用对象: ``` use std::any::Any; fn main() { let value1: Box<dyn Any> = Box::new(42); let value2: Box<dyn Any> = Box::new("hello"); // 使用downcast_ref方法将value1转换为&i32类型的引用 if let Some(number) = value1.downcast_ref::<i32>() { println!("value1 is an i32: {}", number); } // 使用downcast_ref方法将value2转换为&str类型的引用 if let Some(greeting) = value2.downcast_ref::<&str>() { println!("value2 is a string: {}", greeting); } } ``` 在这个示例,我们首先创建了一个存储了整数值的`value1`和一个存储了字符串值的`value2`。然后,我们使用`downcast_ref`方法尝试将它们分别转换为指定类型的引用,并进行了相应的打印输出。 需要注意的是,使用`dyn Any` trait进行类型检查和转换是需要小心谨慎的。在进行类型转换时,必须确保值的实际类型与所期望的类型匹配,否则将会导致运行时错误。 综上所述,虽然Rust没有直接对应于Java的`Object`类型的内建类型,但可以使用`dyn Any` trait来实现类似的功能,通过将具体类型包装到`Box<dyn Any>`,实现存储不同类型值的通用对象,并在需要时进行类型检查和转换。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值