【学习 Rust】

Rust 教程

2021-12-01 17:45:43 更新   浏览量: 569
本章 下一章:安装 Rust

Rust 语言是一种高效、可靠的通用高级语言。其高效不仅限于开发效率,它的执行效率也是令人称赞的,是一种少有的兼顾开发效率和执行效率的语言。

Rust 语言由 Mozilla 开发,最早发布于 2014 年 9 月。Rust 的编译器是在 MIT License 和 Apache License 2.0 双重协议声明下的免费开源软件。截至目前( 2020 年 1 月)最新的编译器版本是 1.41.0。

Rust 官方在线工具: https://play.rust-lang.org/。

Rust 系列文章内容由 Sobin 收集整理。


Rust语言的特点

  • 高性能 - Rust 速度惊人且内存利用率极高。由于没有运行时和垃圾回收,它能够胜任对性能要求特别高的服务,可以在嵌入式设备上运行,还能轻松和其他语言集成。

  • 可靠性 - Rust 丰富的类型系统和所有权模型保证了内存安全和线程安全,让您在编译期就能够消除各种各样的错误。

  • 生产力 - Rust 拥有出色的文档、友好的编译器和清晰的错误提示信息, 还集成了一流的工具 —— 包管理器和构建工具, 智能地自动补全和类型检验的多编辑器支持, 以及自动格式化代码等等。


Rust的应用

Rust 语言可以用于开发:

  • 传统命令行程序 - Rust 编译器可以直接生成目标可执行程序,不需要任何解释程序。
  • Web 应用 - Rust 可以被编译成 WebAssembly,WebAssembly 是一种 JavaScript 的高效替代品。
  • 网络服务器 - Rust 用极低的资源消耗做到安全高效,且具备很强的大规模并发处理能力,十分适合开发普通或极端的服务器程序。
  • 嵌入式设备 - Rust 同时具有JavaScript 一般的高效开发语法和 C 语言的执行效率,支持底层平台的开发。

谁适合阅读本教程?

本教程对于初级的编程知识将默认读者已经掌握,所以如果你阅读本教程,你需要对初级的编程知识有一定的了解(最好已经初识 C/C++ 或 JavaScript 编程语言)。

第一个 Rust 程序

Rust 语言代码文件后缀名为 .rs, 如 w3c教程.rs

实例

fn main() {
    println!("Hello World!");
}
运行预览 »

这个程序有一个变量绑定名称为 x。此绑定的值是一个 Vec<T>,是我们通过在标准库中定义的宏创建的一个'向量'。这个宏被称为 Vec,我们利用!调用宏用。这遵循 Rust 的一般原则:做事情要明确。宏可以有效的做一些比函数调用更复杂的东西,所以他们外观上来看是不一样的。这个符号!也有助于解析,使代码更容易编写,这也很重要。

我们使用 mut 使 x 可变:默认情况下,Rust 中的绑定是不可变的。我们将在后面的例子中改变此向量。

另外值得一提的是,在这里我们不需要标注类型:Rust 是静态类型,我们并不需要再明确标注类型。Rust 有类型推断,用以平衡强大的静态类型和冗长标注类型。

Rust 更倾向于堆栈分配而不是堆分配:x 被直接放置在堆栈中。然而,Vec<T> 类型是在堆上分配的向量元素的空间。如果你不熟悉它们之间的区别,那么你现在可以先忽略它,或者你可以查看章节“堆栈和堆”,来了解详细了解。作为一个系统编程语言,Rust 赋予了你如何分配内存空间的能力,但是在我们开始的阶段,这是并不是一个大问题。

此前,我们提到的“所有权”是铁锈的关键新概念。生锈的说法,X 被说成“自己”的载体。这意味着,当 x 超出范围,载体的存储器将被解除分配。这是由防锈编译确定性完成,而不是通过一个机制,诸如垃圾收集器。换句话说,在防锈,你不叫喜欢的 malloc 函数和释放自己:编译静态判断,当你需要分配或释放内存,并插入这些调用本身。犯错是做人,但编译器永远不会忘记。

前面我们所提到的,“所有权”是 Rust 中的一个非常重要的新概念。按照 Rust 的说法,x 被称为向量“所有”。这意味着当 x 超出范围,向量的内存将被销毁。这样做是由 Rust 的编译器所决定的,而不是通过一种机制(如垃圾收集器)所决定的。换句话说,在 Rust 中,你不需要自己调用函数,如malloc 和 free yourself: 当你需要分配或释放的内存时,编译器会自行静态的决定并插入这些调用函数。犯错是人之常情,但编译器永远不会忘记插入这些调用的函数。

让我们在我们上面的例子中添加另外的一行代码:

    fn main() {
        let mut x = vec!["Hello", "world"];

        let y = &x[0];
    }

我们在此介绍了另外的一种绑定 ,y。在这种情况下,y 是向量第一个元素的一个“引用”。Rust 的引用与其他语言中的指针类似,不同的是有额外的编译时安全检查。特别指出的是,引用与所有权系统通过“借用”来相互作用,而不是通过拥有它。不同的是,当引用超出范围时,它不会释放底层的内存。如果是那样,我们将会释放两次内存,这是显然是不正确的!

让我们来添加第三行代码。表面上来是没错的,但是它会导致一个编译错误:

    fn main() {
        let mut x = vec!["Hello", "world"];

        let y = &x[0];

        x.push("foo");
    }

push 是将一个元素附加到向量组末端的方法。当我们试图编译这个程序时,我们将得到一个错误:

    error: cannot borrow `x` as mutable because it is also borrowed as immutable
        x.push("foo");
        ^
    note: previous borrow of `x` occurs here; the immutable borrow prevents
    subsequent moves or mutable borrows of `x` until the borrow ends
        let y = &x[0];
                 ^
    note: previous borrow ends here
    fn main() {

    }
    ^

Rust 编译器给了很详细的错误,这是其中的一次。如错误解释中所说,虽然我们的绑定是可变的,但我们仍不能调用 push 方法。这是因为我们已经有了一个矢量元素的引用 ,y。当另外的一个引用存在时,改变某些变量是危险的行为,因为我们可能导致引用的无效。在这个特定的例子中,当创建向量时,我们可能只分配了三个元素的空间。添加第四个元素意味着所有这些元素将会被分配一块新的块内存,同时复制旧值到新内存,更新内部指针到新内存。这些工作都没有什么问题。问题是, y 不会更新,所以我们会有一个“悬空指针”。那就不对了。在这种情况下,任何的使用引用 y,将会导致错误,所以编译器已经帮我们捕捉到了这个错误。

那么,我们如何解决这个问题呢?有两种我们可以采用的方法。第一种方法,我们利用拷贝而不是一个引用:

    fn main() {
        let mut x = vec!["Hello", "world"];

        let y = x[0].clone();

        x.push("foo");
    }

在默认情况下,Rust 有移动语义,所有如果我们想要复制一些数据,我们可以调用 clone() 方法。在这个例子中 ,y 不再是存储在 x 中向量的一个引用,而是它的第一个元素“ Hello ”的一个副本。现在没有引用,我们的 push() 方法可以很好地运行。

如果我们真的想用一个引用,我们需要另外一种选择:确保在我们尝试做改变之前,我们的引用跳出其作用域。写法看起来像这样:

    fn main() {
        let mut x = vec!["Hello", "world"];

        {
            let y = &x[0];
        }

        x.push("foo");
    }

我们用一组额外的大括号创建了一个内部作用域。在我们调用 push() 之前,y 已经跳出其作用域。所以这样是可行的。

所有权的概念不仅有利于防止悬空指针,而且有利于解决与其相关的所有问题,如迭代器失效,并发性等等。

参考链接

  • Rust 官方网站:https://www.rust-lang.org/zh-CN
  • Rust 官方文档:https://doc.rust-lang.org/
  • Rust Play:https://play.rust-lang.org/
  • Visual Studio Code:https://code.visualstudio.com/
扫二微码
本章 下一章:安装 Rust

最近更新 免责声明 关于我们

Copyright © 2004-2021 前端教程 qianduan.cn All Rights Reserved   陕ICP备2021014585号-1 陕公网安备 61012402000223 前端教程

友情链接:  前端工具 小明辅助网 php中文网