机器之心报道
参与:思、Jamin
据说很多开发者一天入门 Python,两天上手 Go,但到了 Rust 就会发现画风隐约有些不对。它从语法到特性,似乎都要复杂一些。本文介绍的就是 Rust,作者表示,通过解析大量代码,「半个小时」就能入门 Rust。
Rust 是一门系统编程语言,专注于安全,尤其是并发安全。它支持函数式和命令式以及泛型等编程范式的多范式语言,且 TensorFlow 等深度学习框架也把它作为一个优秀的前端语言。
Rust 在语法上和 C、C++类似,都由花括弧限定代码块,并有相同的控制流关键字,但 Rust 设计者想要在保证性能的同时提供更好的内存安全。Rust 自 2016 年就已经开源了,在各种开发者调查中,它也总能获得「最受欢迎的语言」这一称赞,目前该开源项目已有 42.9K 的 Star 量。
机器之心的读者大多数都非常熟悉 Python,而 Rust 就没那么熟悉了。在 Amos 最近的一篇博文中,他表示如果阅读他的作品,我们半个小时就能入门 Rust。因此在这篇文章中,我们将介绍该博文的主要内容,它并不关注于 1 个或几个关键概念,相反它希望通过代码块纵览 Rust 的各种特性,包括各种关键词与符号的意义。
在 HackNews 上,很多开发者表示这一份入门教程非常实用,Rust 的入门门槛本来就比较高,如果再介绍各种复杂的概念与特性,很容易出现「从入门到劝退」。因此这种从实例代码出发的教程,非常有意义。
从变量说起
let 能绑定变量:
能够正常的使用 :来制定变量的数据类型,以及数据类型注释:
如果你声明一个变量并在初始化之前就调用它,编译器会报错:
然而,这样做完全没问题:
下划线表示特殊的命名,或者更确切地说是「缺失的命名」,它和 Python 的用法有点像:
以下划线开头的命名是常规命名,只是编译器不会警告它们未被使用:
相同命名的单独绑定是可行的,第一次绑定的变量会取消:
Rust 有元组类型,可以将其看作是「不同数据类型值的定长集合」。
如果真的想配置 pair 的数据类型,可以这么写:
元组在赋值时可以被拆解,这在某种程度上预示着它们被分解成各个字段:
当一个函数返还一个元组时会非常有用:
当然,在解构一个元组时,可以只分离它的一部分:
分号表示语句的结尾:
不加分号意味着语句可以跨多行:
函数来了
fn 声明一个函数。下面是一个空函数:
这是一个返还 32 位带符号整数值的函数。箭头表示返还类型:
花括号表示了一个代码块,且拥有其自己的作用域:
代码块也是表示式,表示其计算为一个值。
在一个代码块中,可以有多个语句:
这也是怎么回事「省略函数末尾的分号」等同于加上了 Retrun,这些都是等价的:
if 条件语句也是表达式:
match 匹配器也是一个表达式:
Dots 通常用于访问某个对象的字段:
或者调用对象的方法:
双冒号与此类似,但可对命名空间做相关操作。在此举例中,std 是一个 crate (~ a library),cmp 是一个 module(~ a source file),以及 min 是个函数:
use 指令可用于从其他命名空间中「引入范围」命名:
在 use 指令中,花括号还有另一个含义:「globs」,因此可以同时导入 min 以及 max:
通配符(*)允许从命名空间导入符号:
Types 也是命名空间和方法,它可当作常规函数调用:
str 是一个基元数据类型,但在默认情况下,许多非基元数据类型也在作用域中。
至于为什么可行,因为 Rust 在每个模块的开头都插入了:
再说说结构体
使用 struct 关键字声明结构体:
能够正常的使用结构语句初始化:
有一个快捷方式可以从另一个结构体初始化本结构体的其余字段:
这就是所谓的「结构体更新语法」只能发生在最后一个位置,不能在其后面再跟一个逗号。
注意其余字段可以表示所有字段:
结构体与元组一样,可以被解构。例如一个有效的 let 模式:
让 let 模式在 if 里可当作条件:
多分支的 match 也是条件模式,就像 if let:
match 必须是囊括所有情况的的:至少需要匹配一个条件分支。
如果非常难实现,_那么可以作用一个“包罗万象”的模式:
Type 别名
我们大家可以使用 type 关键字声明另一类型的别名,然后就可以像使用一个真正的类型一样使用这种类型。例如定义 Name 这种数据类型为字符串,后面就可以直接使用 Name 这种类型了。
你可以在方法中声明不同的数据类型:
然后就如同往常那样使用:
默认情况下,声明变量后它就就是不可变的,如下 odd 不能被重新赋值:
不可变的变量声明,其内部也是不可变的,它也不能重新分配值:
mut 可以使变量声明变为可变的:
Traits 描述的是多种数据类型的共同点:
我们大家可以在我们定义的 Type 类型中定义 Traits:
外部类型(foreign type)中定义的 Trait:
impl 模块通常会带有一个 Type 类型,所以在模块内,Self 就表示该类型:
有一些traits只是作为标记,它们并不是说 Type 类型实现了某些方法,它只是表明某些东西能通过Type类型完成。例如,i32 实现了Copy,那么以下代码就是可行的:
下面的代码也是能运行的:
但是 Number 的结构体并不能用于 Copy,所以下面的代码会报错:
同样下面的代码也不会 Work:
但是如果print_number有一个不可变reference,那么 Copy 就是可行的:
如果函数采用了可变reference,那也是可行的,只不过需要在变量声明中带上 mut。
Copy 这类标记型的traits并不带有方法:
现在 Clone 仍然可以用于:
但是Number的值将不会再移除:
有一些traits很常见,它们能够最终靠使用derive 属性自动实现:
看上去,整篇教程都在使用大量代码解释 Rust 的各种语句与用法。可能我们会感觉博客结构不是太明确,但是实例驱动的代码学习确实更加高效。尤其是对于那些有一些编程基础的同学,他们可以快速抓住 Rust 语言的特点与逻辑。
最后,这篇文章并没有展示博客所有的内容,如果读者想真正入门 Rust 语言,推荐可以查阅原博客。
原文地址:https://fasterthanli.me/blog/2020/a-half-hour-to-learn-rust/
本文为机器之心编译,转载请联系本公众号获得授权。
------------------------------------------------