陈天 · Rust 编程第一课
陈天
Tubi TV 研发副总裁
新⼈⾸单¥59
2974 人已学习
课程目录
已更新 16 讲 / 共 41 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词|让Rust成为你的下一门主力语言
免费
前置篇 (3讲)
01|内存:值放堆上还是放栈上,这是一个问题
02|串讲:编程开发中,那些你需要掌握的基本概念
加餐| Rust真的值得我们花时间学习么?
基础篇 (12讲)
03|初窥门径:从你的第一个Rust程序开始!
04|get hands dirty:来写个实用的CLI小工具
05|get hands dirty:做一个图片服务器有多难?
06|get hands dirty:SQL查询工具怎么一鱼多吃?
07|所有权:值的生杀大权到底在谁手上?
08|所有权:值的借用是如何工作的?
09|所有权:一个值可以有多个所有者么?
10|生命周期:你创建的值究竟能活多久?
11|内存管理:从创建到消亡,值都经历了什么?
加餐|愚昧之巅:你的Rust学习常见问题汇总
12|类型系统:Rust的类型系统有什么特点?
13|类型系统:如何使用trait来定义接口?
陈天 · Rust 编程第一课
15
15
1.0x
00:00/00:00
登录|注册

12|类型系统:Rust的类型系统有什么特点?

你好,我是陈天。今天我们就开始类型系统的学习。
如果你用 C/Golang 这样不支持泛型的静态语言,或者用 Python/Ruby/JavaScript 这样的动态语言,这个部分可能是个难点,希望你做好要转换思维的准备;如果你用 C++/Java/Swift 等支持泛型的静态语言,可以比较一下 Rust 和它们的异同。
其实在之前的课程中,我们已经写了不少 Rust 代码,使用了各种各样的数据结构,相信你对 Rust 的类型系统已经有了一个非常粗浅的印象。那类型系统到底是什么?能用来干什么?什么时候用呢?今天我们就来一探究竟。
作为一门语言的核心要素,类型系统很大程度上塑造了语言的用户体验以及程序的安全性。为什么这么说?因为,在机器码的世界中,没有类型而言,指令仅仅和立即数或者内存打交道,内存中存放的数据都是字节流。
所以,可以说类型系统完全是一种工具,编译器在编译时对数据做静态检查,或者语言在运行时对数据做动态检查的时候,来保证某个操作处理的数据是开发者期望的数据类型。
现在你是不是能理解,为什么 Rust 类型系统对类型问题的检查格外严格(总是报错)。

类型系统基本概念与分类

在具体讲 Rust 的类型系统之前,我们先来澄清一些类型系统的概念,在基本理解上达成一致。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/1000字
划线
笔记
复制
该试读文章来自付费专栏《陈天 · Rust 编程第一课》,如需阅读全部文章,
请订阅文章所属专栏新⼈⾸单¥59
立即订阅
登录 后留言

精选留言(8)

  • qinsi
    不确定最后的代码里为什么要用泛型,改了几个不同的版本:

    https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f21447a10eac4e3b77d39aefbd93ab6b

    https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=2d8037d4059efe15c3d15cfa2267bb70

    https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c57f2abe9756bf021241c63e1b944af8
    2021-09-20
    1
  • Marvichov
    W会被resolve成一个实际类型, 也就是`BufferWriter::new(stream)`的类型. 而rust要求W是一个泛型. 我的下面的改动有点多...

    有一点我就有点不明白了, 声明的时候都说了W: Write, 为啥impl的时候还要说一遍呢 (不说不让编译)?

    ```
    use std::io::{BufWriter, Write};
    use std::net::TcpStream;

    #[derive(Debug)]
    struct MyWriter<W>
    where
        W: Write,
    {
        writer: BufWriter<W>,
    }

    impl<W: Write> MyWriter<W> {
        pub fn new(stream: W) -> Self {
            Self {
                writer: BufWriter::new(stream),
            }
        }

        pub fn write(&mut self, buf: &str) -> std::io::Result<()> {
            self.writer.write_all(buf.as_bytes())
        }
    }

    fn main() {
        let addr = "127.0.0.1:8080";
        let stream = TcpStream::connect(addr).unwrap();
        let mut writer = MyWriter::new(stream);
        writer.write("hello world!").unwrap();
    }
    ```

    作者回复: 正确!

    至于声明 struct 和 impl 时为何要重复声明,我觉得有两个原因:1. 文中谈到声明 struct 时的约束和实现时的约束可以不一样。2. 这样编译和类型声明代码和实现代码以及 类型对 trait 的实现代码可以彼此独立。

    2021-09-20
    1
  • 记事本
    改进了看符不符合要求,记事本写的
    use std::io::{BufWriter,Write};
    use std::net::TcpStream;

    #[derive(Debug)]
    struct MyWriter<W:Write>{
        writer:BufWriter<W>
    }

    impl <W:Write> MyWriter<W> {
        pub fn new(stram:W) ->Self {
            Self {
                writer: BufWriter::new(stram)
            }
        }

        pub fn write(&mut self,s:&str) ->std::io::Result<()> {
            self.writer.write_all(s.as_bytes())
        }
    }
    fn main() {

        let addr = "127.0.0.1:8001";
        let stream = TcpStream::connect(addr).unwrap();
        let mut s = MyWriter::new(stream);
        s.write("hello world");

    }

    作者回复: 👍 正确

    2021-09-21
  • Fenix
    想问下,看到各种文章都说python是强类型的语言呀

    作者回复: 我文中没有说 python 是弱类型啊。强类型和弱类型的划分并没有严格的标准,其中一个标准是看会不会自动发生隐式转换。

    2021-09-21
  • 罗杰
    追老师的更新比追剧有趣多了,配合张老师的视频课,还有陈老师 B 站的 Rust 培训视频,真的很舒服。

    作者回复: 哈哈,感谢支持!

    2021-09-20
  • lisiur
    思考题代码报错的主要原因是,实现 new 方法时,对泛型的约束要求要满足 W: Write,而 new 的声明返回值是 Self,也就是说 self.wirter 必须是 W: Write 类型(泛型),但实际返回值是一个确定的类型 BufWriter<TcpStream>,这不满足要求。

    修改方法有这么几个思路

    1. 修改 new 方法的返回值

    ```rust
    impl<W: Write> MyWriter<W> {
        pub fn new(addr: &str) -> MyWriter<BufWriter<TcpStream>> {
            let stream = TcpStream::connect(addr).unwrap();
            MyWriter {
                writer: BufWriter::new(stream),
            }
        }
    }

    fn main() {
        let mut writer = MyWriter::<BufWriter<TcpStream>>::new("127.0.0.1:8080");
        writer.write("hello world!");
    }
    ```

    2. 对确定的类型 MyWriter<BufWriter<TcpStream>>实现 new 方法:

    ```rust
    impl MyWriter<BufWriter<TcpStream>> {
        pub fn new(addr: &str) -> Self {
            let stream = TcpStream::connect(addr).unwrap();
            Self {
                writer: BufWriter::new(stream),
            }
        }
    }

    fn main() {
        let mut writer = MyWriter::new("127.0.0.1:8080");
        writer.write("hello world!");
    }
    ```

    3. 修改 new 方法的实现,使用依赖注入

    ```rust
    impl<W: Write> MyWriter<W> {
        pub fn new(writer: W) -> Self {
            Self {
                writer,
            }
        }
    }

    fn main() {
        let stream = TcpStream::connect("127.0.0.1:8080").unwrap();
        let mut writer = MyWriter::new(BufWriter::new(stream));
        writer.write("hello world!");
    }
    ```

    PS:第2种解法还可以对不同具体类型实现多个new方法:

    ```rust
    impl MyWriter<BufWriter<TcpStream>> {
        pub fn new(addr: &str) -> Self {
            let stream = TcpStream::connect(addr).unwrap();
            Self {
                writer: BufWriter::new(stream),
            }
        }
    }

    impl MyWriter<File> {
        pub fn new(addr: &str) -> Self {
            let file = File::open(addr).unwrap();
            Self { writer: file }
        }
    }

    fn main() {
        let mut writer = MyWriter::<BufWriter<TcpStream>>::new("127.0.0.1:8080");
        writer.write("hello world!");

        let mut writer = MyWriter::<File>::new("/etc/hosts");
        writer.write("127.0.0.1 localhost");
    }
    ```

    作者回复: 非常棒!最佳答案!

    2021-09-20
  • qinsi
    为什么中间的图上写着Haskell/ML不能自动推导...类型推导可是ML系语言的标配
    2021-09-20
  • c4f
    不能编译是因为第 14 行 writer 期待的值的类型也是 W,但收到的却是 BufWriter<TcpStream> 因此不匹配。
    参考 MyReader 进行了修改,不过这种方法修改了 MyWriter 对外提供的接口(new),应该还有别的方法,不过没找出来 hh

    ```

    use std::io::{BufWriter, Write};
    use std::net::TcpStream;

    #[derive(Debug)]
    struct MyWriter<W> {
        writer: W,
    }

    impl<W> MyWriter<W> {
        pub fn new(writer: W) -> Self {
            Self {
                writer
            }
        }
    }

    impl<W: Write> MyWriter<W> {
        pub fn write(&mut self, buf: &str) -> std::io::Result<()> {
            self.writer.write_all(buf.as_bytes())
        }
    }

    fn main() {
        let mut writer = MyWriter::new(
            BufWriter::new(TcpStream::connect("127.0.0.1:8080").unwrap())
        );
        writer.write("hello world!").unwrap();
    }
    ```

    另外还有一个问题想请教老师:通过在冒号后面对范型进行限制和使用 where 对范型进行限制是否是等价的。比如下面的例子

    ```
    impl<W: Write> MyWriter<W> {
        ...
    }

    impl<W> MyWriter<W>
    where
        W: Write
    {
        ...
    }
    ```

    作者回复: 这么改是可以的!原代码的接口本身就有问题,所以需要动接口。对泛型的限制,用 : 限制和 where 是等价的,where 更灵活一些,表述更优雅一些。一般短小的限制,我们用 : 就足够,但对多个参数的复杂的限制,用 where 可读性会更强。

    2021-09-20
收起评论
8
返回
顶部