利用 trait 实现多态

我在书上看到基于 std::io::Write 的示例,它是一个 trait 类型,内部声明了一些方法。和 go 语言不同,rust 中类型必须明确实现 trait 类型,而 go 语言属于 duck 模式。

std::io::Write

下面的例子中调用 write_all 方式来演示,write_all 也是 Write trait 声明要实现的方法之一。
利用 trait 实现多态_第1张图片
示例中 buf 的类型是 Vec 类型,调用 write_all 向其中写入数据,然后将 buf 转换为 &str 类型打印输出。

但下面的代码会编译报错,提示 method not found,buf 实现了 Write trait,但编译器找不到这个 write_all 方法。问题的原因:特型本身必须在作用域中,否则,特型的所有方法都是隐藏的。我理解,这属于 rust 类型和 trait 的自动绑定过程。

解决编译问题的方法非常简单,只需要在文件头部导入 use std::io::Write; 就可以了。

use std::str;
// use std::io::Write;

fn main() {
    let mut buf: Vec<u8> = vec![];
    let _ = buf.write_all(b"hello");

    let s = match str::from_utf8(buf.as_slice()) {
        Ok(v) => v,
        Err(e) => panic!("Invalid UTF-8 sequence:{}", e),
    };

    println!("{}", s);
}

下面我们看下 write_all 方法提供的 example,创建一个文件,然后向其中写入内容。buffer 对象调用了结构体声明的 write_all 方法。这里就明显区别于 Vec 类型,File 调用的过程没有在头部声明 trait 也执行成功了。

use std::io::prelude::*;
use std::fs::File;

fn main() -> std::io::Result<()> {
    let mut buffer = File::create("foo.txt")?;

    buffer.write_all(b"some bytes")?;
    Ok(())
}

我们使用 trait 主要是为了设计通用性考虑,会将类型声明为 trait 类型。因为 std::fs::File 实现了 trait 特性,我们尝试将 buffer 对象声明 trait 类型。如下代码所示,遗憾的是,rust 不允许声明 Write 的变量类型。理由是:变量大小必须在编译时就确定,而实现 Write 的类型可以是任意大小。

下面的代码编译无法通过:

use std::io::prelude::*;
use std::fs::File;
use std::io::Write;

fn main() -> std::io::Result<()> {
    let mut buffer: Write = File::create("foo.txt")?;

    buffer.write_all(b"some bytes")?;
    Ok(())
}

要想声明 Write trait 类型,必须使用引用,因为指针的大小是固定的。Go 语言的接口类型可以接收任意类型的值,也是通过指针来做的类型大小屏蔽。

内部实现上,&Write 和 interface 的实现就非常接近,都是内部包含两个指针,分别指向实际的值和对应的类型。实际的值比较好理解,关键是对应的类型。

use std::fs::File;
use std::io::Write;

fn main() -> std::io::Result<()> {
    let mut foo = File::create("foo.txt")?;
    let buffer: &mut dyn Write = &mut foo;

    buffer.write_all(b"some bytes")?;
    Ok(())
}

你可能感兴趣的:(rust,编程开发,rust)