编程语言人门第一课,必须得是 hello world程序。 我们先来看看 Rust的 hello world是 什么样子:

// hello world.rs fn main() {
let s =”hello world!"; println! ({}, s);

对于这样一个简单的示例程序,我们并没有使用 cargo创建工程 , 关系。编译就直接使用 rustc 即可,其他所有选项使用默认值:

rustc hello world.rs

因为没有复杂的依赖可看到本地文件夹中生成了一个名为 hello world 的可 执行程序 。 执行. /hello world程序,可以看见控制台上输出了 hello world! 字符串。 恭喜读者,第一个 Rust程 序已经运行成功了!

我们来分析一下这个最简单的程序 。

  • 1 )一般 Rust源代码的后缀名使用 .rs表示 。 源码一定要注意使用 utf-8 编码。
  • 2 )第一行是注释语句, Rust 的注释是 C 语言系列风格的,行注释采用 // 开头,块注释使用 //包围。 它还支持更高级的文档注释 ,将在后文中详细展开说明 。
    1. fn是一个关键宇(keyword),函数定义必须以这个关键字开头。 函数体使用大括号 来包含 。 也是单词在mction 的缩写 ,在 Rust 中,设计者比较偏向使用单词缩写,即使是关键 字也不例外 。 在代码风格上,某些读者可能开始会有点不习惯 。 但总体而言,这只是个审美
      偏好而已,不必过于纠结,习惯就好 。
  • 4 )默认情况下, main 函数是可执行程序的人口点,它是一个无参数,无返 回值的函数 。
    如果我们要定义的函数有参数和返回值 , 可以使用以下语法(参数列表使用逗号分开,冒号 后面是类型,返回值类型使用-〉符号分隔):
fn Foo( inputl : i32, input2 : u32) -> i32 {
    1. 局部变量声明使用 let关键字开头,用双引号包含起来的部分是字符串常量。 Rust 是静态强类型语言,所有的变量都有严格的编译期语法检查。 关于Rust的变量和类型系统将 在后文详细说明 。
    1. 每条语句使用分号结尾。 语句块使用大括号。 空格、换行和缩进不是语法规则的一 部分 。 这都是明显的 C 语言系列的风格 。
      最简单的标准输出是使用 println ! 宏来完成 。 请大家一定注意 println 后面的感叹 号, 它代 表这是一个宏,而不是一个函数 。 Rust 中的宏与 CIC++ 中的宏是完全不 一样 的东西 。 简单点说,可 以把它理解为一种安全版的编译期语法扩展 。 这里之所以使用宏,而不是 函数,是因为标准输出宏可以完成编译期格式检查 , 更加安全。

从这个小程序的惊鸿一瞥中 大家可以看到 Rust 的语法主要还 是 C 系列的语法风格 。 对于熟悉 CIC++I Java I C# I PHP I JavaScript等语言的读者来说,会看到许多熟悉的身影。

Prelude

Rust 的代码从逻辑上是分 crate 和 mod 管理的 。 所谓 crate 大家可以理解为“项目” 。 每 个 crate 是一个完整的编译单元,它可以生成为一个 lib 或者 exe 可执行文件 。 而在 crate 内 部, 则是由 mod这个概念管理的,所谓 mod大家可以理解为 namespace。 我们可以使用 use 语句把其他模块中的内容引入到当前模块中来 。 关于 Rust模块系统的详细说明,可参见本书 第五部分 。
Rust 有一个极简标准库, 叫作 std,除了极少数嵌入式系统 下无 法使用标准库之外,绝 大部分情况下,我们都需要用到标准库里面的东西。 为了给大家减少麻烦, Rust编译器对标 准库有特殊处理。 默认情况下,用户不需要手动添加对标准库的依赖 ,编译器会自动引人对 标准库的依赖。 除此之外,标准库中的某些 type、 trait、 function、 macro等实在是太常用了。 每次都写 use语句确实非常无聊,因此标准库提供了 一个 std: : prelude 模块,在这个模 块中导出了一些最常见的类型、trait等东西 编译器会为用户写的每个 crate 自动插入一句话:

use std: :prelude::*;

这样,标准库里面的这些最重要的类型、 trait等名字就可以直接使用,而无须每次都写全称或者 use语句。

Prelude模块 的源码在 src/libstd/prelude/文件夹下。 我们可以看到,目前的mod.rs中, 直接导出了 vl模块中的内容, 而vl.rs中, 则是编译器为我们自动导人的相关 trait 和类 型 。


Format 格式详细说明

在后面的内容中,我们还会大量使用 println!宏,因此提前介绍一下这个宏的基本用 法。 跟 C语言的 printf函数类似,这个宏也支持各种格式控制,示例如下:

fn main() { println!({}, 1);
println! ({ :o},
println! ({ :x },
println!( ;'{:X},
println! ({ :p},& O); println! ({ :b }, 15); println! ({ :e}, 10000f32);
// 默认用法,打印Display // 八进制
//十六进 制 小 写 //十六进制 大写
// 指针
//二 进制
// 科学计数 ( 小写 )
println! ({ :E}, 10000£32}; println! ({:?},”test”);
println!({:#?},(”test!,”test2”));
println!({a} {b} {b}, a =X, b =Y); // 命名参数

Rust标准库中之所以设计了这么一个宏来做标准输出,主要是为了更好地错误检查 。 大 家可 以试试,如果出现参数个数、格式等各种原因不匹配会直接导致编译错误 。 而函数则不 具备字符串格式化的静态检查功能,如果出现了不匹配的情况 ,只能是运行期错误。 这个宏 最终还是调用 了 std:: io 模块 内提供 的一些函数来完成的 。 如果用户需要更精细地控制标 准输 出操作 , 也可以直接调用标准库来完成 。


变量声明

Rust的变量必须先声明后使用。 对于局部变量,最常见的声明语法为:

let variable : i32 = 100;

与传统的 CIC++语言相 比, Rust 的变量声明语法不同 。 这样设计主要有以下几个方面的考虑。

    1. 语法分析更容易
      从语法分析的角度来说, Rust 的变量声 明语法比 CIC++ 语言的简单,局部变量声明一定 是 以关键字 let 开头,类型一定是跟在冒号:的后面 。 语法歧义更少,语法分析器更容易 编写。
    1. 方便引入类型推导功能
      Rust 的变量声明的一个重要特点是:要声明的变量前置,对它的类型描述后置 。 这也是 吸取了其他语言的教训 后的结果 。 因为在变量声明语句中,最重要的是变量本身,而类型其 实是个 附属的额外描述,并非必不可少的部分 。 如果我们可以通过上下文环境由编译器自动 分析出这个变量的类型 ,那么这个类型描述完全可以省略不写 。 Rust一开始的设计就考虑了 类型自动推导功能,因此类型后 置 的语法更合适 。
    1. 模式解构
      let 语句不光是局部变 量声 明语句,而且具有 pattern destructure (模式解构) 的功能 。 关于“模式解构”的内容在后面的章节会详细描述 。
      实际上, 包括 C++ I C# I Java 等传统编程语言都开始逐步引人这种声明语法,目的是相 似的。

Rust 中声明变量缺省是“只读”的,比如如下程序:

fn main() { let x = 5;
x = 10;

会得到“re-assignmentofimmutable variable 、X、”这样的编译错误。 如果我们需要让变量是可写的,那么需要使用 rnut 关键宇 :

let mut x = 5; // mut x: i32 x = 10;

此时\ 变量x才是可读写的。

实际上, let 语句在此处引入了一个模式解构,我们不能把 let 而应该将 rnut x 视为一个组合 。rnut x是一个“模式”,我们还可以用这种方式同时声明多个变量:

 let (mut a, mut b) = (1, 2);
let Point { x: ref a, y: ref b} = p;

rnut 视为 一个组合,
其中,赋值号左边的部分是一个“模式”,第一行代码是对 tuple 的模式解构,第二行代 - 码是对结构体的模式解构。 所以,在 Rust中,一般把声明的局部变量并初始化的语句称为 “变量绑定”,强调的是“绑定”的含义,与 CIC++ 中的“赋值初始化”语句有所区别 。
Rust中,每个变量必须被合理初始化之后才能被使用。 使用未初始化变量这样的错误,在 Rust中是不可能出现的 (利用unsafe做hack除外)。 如下这个简单的程序, 也不能编译通过:

fn main() {
let x: i32;
println! ({}, x); 错误信息为 :
error: use of possibly uninitialized variable : 

编译器会帮我们做一个执行路径的静态分析,确保变量在使用前一定被初始化:

fn test(condition: bool) {
let x: i32; II 声明 x, 不必使用 mut 修饰 if condition {
x = 1; // 初始化 x,不需要 x 是 mut 的, 因为这是初始化,不是修改
println! ({}, x );
// 如果条件不满足 ,x 没有被初始化
// 但是没关系 , 只要这里不使用 x 就没事

我们不能在表达式中使用下划线来作为普通变量使用。 下划线表达的含义是“忽略这个变量绑定,后面不会再用到了” 。 在后面讲析构的 时候 , 还会提到这一点 。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐