据说很多开发者一天入门 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 能绑定变量:
- let x; // declare "x"
 - x = 42; // assign 42 to "x"
 - let x = 42; // combined in one line
 
可以使用 :来制定变量的数据类型,以及数据类型注释:
- let x: i32; // `i32` is a signed 32-bit integer
 - x = 42;
 - // there's i8, i16, i32, i64, i128
 - // also u8, u16, u32, u64, u128 for unsigned
 - let x: i32 = 42; // combined in one line
 
如果你声明一个变量并在初始化之前就调用它,编译器会报错:
- let x;
 - foobar(x); // error: borrow of possibly-uninitialized variable: `x`
 - x = 42;
 
然而,这样做完全没问题:
- let x;
 - x = 42;
 - foobar(x); // the type of `x` will be inferred from here
 
下划线表示特殊的命名,或者更确切地说是「缺失的命名」,它和 Python 的用法有点像:
- // this does *nothing* because 42 is a constant
 - let _ = 42;
 - // this calls `get_thing` but throws away its result
 - let _ = get_thing();
 
以下划线开头的命名是常规命名,只是编译器不会警告它们未被使用:
- // we may use `_x` eventually, but our code is a work-in-progress
 - // and we just wanted to get rid of a compiler warning for now.
 - let _x = 42;
 
相同命名的单独绑定是可行的,第一次绑定的变量会取消:
- let x = 13;
 - let x = x + 3;
 - // using `x` after that line only refers to the second `x`,
 - // the first `x` no longer exists.
 
Rust 有元组类型,可以将其看作是「不同数据类型值的定长集合」。
- let pair = ('a', 17);
 - pair.0; // this is 'a'
 - pair.1; // this is 17
 
如果真的想配置 pair 的数据类型,可以这么写:
- let pair: (char, i32) = ('a', 17);
 
元组在赋值时可以被拆解,这意味着它们被分解成各个字段:
- let (some_char, some_int) = ('a', 17);
 - // now, `some_char` is 'a', and `some_int` is 17
 
当一个函数返还一个元组时会非常有用:
- let (left, right) = slice.split_at(middle);
 
当然,在解构一个元组时,可以只分离它的一部分:
- let (_, right) = slice.split_at(middle);
 
分号表示语句的结尾:
- let x = 3;
 - let y = 5;
 - let z = y + x;
 
不加分号意味着语句可以跨多行:
- let x = vec![1, 2, 3, 4, 5, 6, 7, 8]
 - .iter()
 - .map(|x| x + 3)
 - .fold(0, |x, y| x + y);
 
函数来了
fn 声明一个函数。下面是一个空函数:
- fn greet() {
 - println!("Hi there!");
 - }
 
这是一个返还 32 位带符号整数值的函数。箭头表示返还类型:
- fn fair_dice_roll() -> i32 {
 - 4
 - }
 
花括号表示了一个代码块,且拥有其自己的作用域:
- // This prints "in", then "out"
 - fn main() {
 - let x = "out";
 - {
 - // this is a different `x`
 - let x = "in";
 - println!(x);
 - }
 - println!(x);
 - }
 
代码块也是表示式,表示其计算为一个值。
- // this:
 - let x = 42;
 - // is equivalent to this:
 - let x = { 42 };
 
在一个代码块中,可以有多个语句:
- let x = {
 - let y = 1; // first statement
 - let z = 2; // second statement
 - y + z // this is the *tail* - what the whole block will evaluate to
 - };
 
这也是为什么「省略函数末尾的分号」等同于加上了 Retrun,这些都是等价的:
- fn fair_dice_roll() -> i32 {
 - return 4;
 - }
 - fn fair_dice_roll() -> i32 {
 - 4
 - }
 
if 条件语句也是表达式:
- fn fair_dice_roll() -> i32 {
 - if feeling_lucky {
 - 6
 - } else {
 - 4
 - }
 - }
 
match 匹配器也是一个表达式:
- fn fair_dice_roll() -> i32 {
 - match feeling_lucky {
 - true => 6,
 - false => 4,
 - }
 - }
 
Dots 通常用于访问某个对象的字段:
- let a = (10, 20);
 - a.0; // this is 10
 - let amos = get_some_struct();
 - amos.nickname; // this is "fasterthanlime"
 
或者调用对象的方法:
- let nick = "fasterthanlime";
 - nick.len(); // this is 14
 
双冒号与此类似,但可对命名空间进行操作。在此举例中,std 是一个 crate (~ a library),cmp 是一个 module(~ a source file),以及 min 是个函数:
- let least = std::cmp::min(3, 8); // this is 3
 
use 指令可用于从其他命名空间中「引入范围」命名:
- use std::cmp::min;
 - let least = min(7, 1); // this is 1
 
在 use 指令中,花括号还有另一个含义:「globs」,因此可以同时导入 min 以及 max:
- // this works:
 - use std::cmp::min;
 - use std::cmp::max;
 - // this also works:
 - use std::cmp::{min, max};
 - // this also works!
 - use std::{cmp::min, cmp::max};
 
通配符(*)允许从命名空间导入符号:
- // this brings `min` and `max` in scope, and many other things
 - use std::cmp::*;
 
Types 也是命名空间和方法,它可以作为常规函数调用:
- let x = "amos".len(); // this is 4
 - let x = str::len("amos"); // this is also 4
 
str 是一个基元数据类型,但在默认情况下,许多非基元数据类型也在作用域中。
- // `Vec` is a regular struct, not a primitive type
 - let v = Vec::new();
 - // this is exactly the same code, but with the *full* path to `Vec`
 - let v = std::vec::Vec::new()
 
至于为什么可行,因为 Rust 在每个模块的开头都插入了:
- use std::prelude::v1::*;
 
再说说结构体
使用 struct 关键字声明结构体:
- struct Vec2 {
 - x: f64, // 64-bit floating point, aka "double precision"
 - y: f64,
 - }
 
可以使用结构语句初始化:
- let v1 = Vec2 { x: 1.0, y: 3.0 };
 - let v2 = Vec2 { y: 2.0, x: 4.0 };
 - // the order does not matter, only the names do
 
有一个快捷方式可以从另一个结构体初始化本结构体的其余字段:
- let v3 = Vec2 {
 - x: 14.0,
 - ..v2
 - };
 
这就是所谓的「结构体更新语法」只能发生在最后一个位置,不能在其后面再跟一个逗号。
注意其余字段可以表示所有字段:
- let v4 = Vec2 { ..v3 };
 
结构体与元组一样,可以被解构。例如一个有效的 let 模式:
- let (left, right) = slice.split_at(middle);
 - let v = Vec2 { x: 3.0, y: 6.0 };
 - let Vec2 { x, y } = v;
 - // `x` is now 3.0, `y` is now `6.0`
 - let Vec2 { x, .. } = v;
 - // this throws away `v.y`
 
让 let 模式在 if 里可以作为条件:
- struct Number {
 - odd: bool,
 - value: i32,
 - }
 - fn main() {
 - let one = Number { odd: true, value: 1 };
 - let two = Number { odd: false, value: 2 };
 - print_number(one);
 - print_number(two);
 - }
 - fn print_number(n: Number) {
 - if let Number { odd: true, value } = n {
 - println!("Odd number: {}", value);
 - } else if let Number { odd: false, value } = n {
 - println!("Even number: {}", value);
 - }
 - }
 - // this prints:
 - // Odd number: 1
 - // Even number: 2
 
多分支的 match 也是条件模式,就像 if let:
- fn print_number(n: Number) {
 - match n {
 - Number { odd: true, value } => println!("Odd number: {}", value),
 - Number { odd: false, value } => println!("Even number: {}", value),
 - }
 - }
 - // this prints the same as before
 
match 必须是囊括所有情况的的:至少需要匹配一个条件分支。
- fn print_number(n: Number) {
 - match n {
 - Number { value: 1, .. } => println!("One"),
 - Number { value: 2, .. } => println!("Two"),
 - Number { value, .. } => println!("{}", value),
 - // if that last arm didn't exist, we would get a compile-time error
 - }
 - }
 
如果非常难实现,_ 那么可以作用一个“包罗万象”的模式:
- fn print_number(n: Number) {
 - match n.value {
 - 1 => println!("One"),
 - 2 => println!("Two"),
 - _ => println!("{}", n.value),
 - }
 - }
 
Type 别名
我们可以使用 type 关键字声明另一类型的别名,然后就可以像使用一个真正的类型一样使用这种类型。例如定义 Name 这种数据类型为字符串,后面就可以直接使用 Name 这种类型了。
你可以在方法中声明不同的数据类型:
- struct Number {
 - odd: bool,
 - value: i32,
 - }
 - impl Number {
 - fn is_strictly_positive(self) -> bool {
 - self.value > 0
 - }
 - }
 
然后就如同往常那样使用:
- fn main() {
 - let minus_two = Number {
 - odd: false,
 - value: -2,
 - };
 - println!("positive? {}", minus_two.is_strictly_positive());
 - // this prints "positive? false"
 - }
 
默认情况下,声明变量后它就就是不可变的,如下 odd 不能被重新赋值:
- fn main() {
 - let n = Number {
 - odd: true,
 - value: 17,
 - };
 - n.odd = false; // error: cannot assign to `n.odd`,
 - // as `n` is not declared to be mutable
 - }
 
不可变的变量声明,其内部也是不可变的,它也不能重新分配值:
- fn main() {
 - let n = Number {
 - odd: true,
 - value: 17,
 - };
 - n = Number {
 - odd: false,
 - value: 22,
 - }; // error: cannot assign twice to immutable variable `n`
 - }
 
mut 可以使变量声明变为可变的:
- fn main() {
 - let mut n = Number {
 - odd: true,
 - value: 17,
 - }
 - n.value = 19; // all good
 - }
 
Traits 描述的是多种数据类型的共同点:
- trait Signed {
 - fn is_strictly_negative(self) -> bool;
 - }
 
我们可以在我们定义的 Type 类型中定义 Traits:
- impl Signed for Number {
 - fn is_strictly_negative(self) -> bool {
 - self.value < 0
 - }
 - }
 - fn main() {
 - let n = Number { odd: false, value: -44 };
 - println!("{}", n.is_strictly_negative()); // prints "true"
 - }
 
外部类型(foreign type)中定义的 Trait:
- impl Signed for i32 {
 - fn is_strictly_negative(self) -> bool {
 - self < 0
 - }
 - }
 - fn main() {
 - let n: i32 = -44;
 - println!("{}", n.is_strictly_negative()); // prints "true"
 - }
 
impl 模块通常会带有一个 Type 类型,所以在模块内,Self 就表示该类型:
- impl std::ops::Neg for Number {
 - type Output = Self;
 - fn neg(self) -> Self {
 - Self {
 - value: -self.value,
 - odd: self.odd,
 - }
 - }
 - }
 
有一些traits只是作为标记,它们并不是说 Type 类型实现了某些方法,它只是表明某些东西能通过Type类型完成。例如,i32 实现了Copy,那么以下代码就是可行的:
- fn main() {
 - let a: i32 = 15;
 - let b = a; // `a` is copied
 - let c = a; // `a` is copied again
 - }
 
下面的代码也是能运行的:
- fn print_i32(x: i32) {
 - println!("x = {}", x);
 - }
 - fn main() {
 - let a: i32 = 15;
 - print_i32(a); // `a` is copied
 - print_i32(a); // `a` is copied again
 - }
 
但是 Number 的结构体并不能用于 Copy,所以下面的代码会报错:
- fn main() {
 - let n = Number { odd: true, value: 51 };
 - let m = n; // `n` is moved into `m`
 - let o = n; // error: use of moved value: `n`
 - }
 
同样下面的代码也不会 Work:
- fn print_number(n: Number) {
 - println!("{} number {}", if n.odd { "odd" } else { "even" }, n.value);
 - }
 - fn main() {
 - let n = Number { odd: true, value: 51 };
 - print_number(n); // `n` is moved
 - print_number(n); // error: use of moved value: `n`
 - }
 
但是如果print_number有一个不可变reference,那么 Copy 就是可行的:
- fn print_number(n: &Number) {
 - println!("{} number {}", if n.odd { "odd" } else { "even" }, n.value);
 - }
 - fn main() {
 - let n = Number { odd: true, value: 51 };
 - print_number(&n); // `n` is borrowed for the time of the call
 - print_number(&n); // `n` is borrowed again
 - }
 
如果函数采用了可变reference,那也是可行的,只不过需要在变量声明中带上 mut。
- fn invert(n: &mut Number) {
 - n.value = -n.value;
 - }
 - fn print_number(n: &Number) {
 - println!("{} number {}", if n.odd { "odd" } else { "even" }, n.value);
 - }
 - fn main() {
 - // this time, `n` is mutable
 - let mut n = Number { odd: true, value: 51 };
 - print_number(&n);
 - invert(&mut n); // `n is borrowed mutably - everything is explicit
 - print_number(&n);
 - }
 
Copy 这类标记型的traits并不带有方法:
- // note: `Copy` requires that `Clone` is implemented too
 - impl std::clone::Clone for Number {
 - fn clone(&self) -> Self {
 - Self { ..*self }
 - }
 - }
 - impl std::marker::Copy for Number {}
 
现在 Clone 仍然可以用于:
- fn main() {
 - let n = Number { odd: true, value: 51 };
 - let m = n.clone();
 - let o = n.clone();
 - }
 
但是Number的值将不会再移除:
- fn main() {
 - let n = Number { odd: true, value: 51 };
 - let m = n; // `m` is a copy of `n`
 - let o = n; // same. `n` is neither moved nor borrowed.
 - }
 
有一些traits很常见,它们可以通过使用derive 属性自动实现:
- #[derive(Clone, Copy)]
 - struct Number {
 - odd: bool,
 - value: i32,
 - }
 - // this expands to `impl Clone for Number` and `impl Copy for Number` blocks.
 
看上去,整篇教程都在使用大量代码解释 Rust 的各种语句与用法。可能我们会感觉博客结构不是太明确,但是实例驱动的代码学习确实更加高效。尤其是对于那些有一些编程基础的同学,他们可以快速抓住 Rust 语言的特点与逻辑。
最后,这篇文章并没有展示博客所有的内容,如果读者想真正入门 Rust 语言,推荐可以查阅原博客。
                新闻标题:半小时入门Rust,这是一篇Rust代码风暴
                
                新闻来源:http://www.csdahua.cn/qtweb/news18/523118.html
            
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网