半小时入门Rust,这是一篇Rust代码风暴

栏目: IT技术 · 发布时间: 4年前

内容简介:Rust 是一门系统编程语言,专注于安全,尤其是并发安全。它支持函数式和命令式以及泛型等编程范式的多范式语言,且 TensorFlow 等深度学习框架也把它作为一个优秀的前端语言。Rust 在语法上和 C、C++类似,都由花括弧限定代码块,并有相同的控制流关键字,但 Rust 设计者想要在保证性能的同时提供更好的内存安全。Rust 自 2016 年就已经开源了,在各种开发者调查中,它也总能获得「最受欢迎的语言」这一称赞,目前该开源项目已有 42.9K 的 Star 量。

半小时入门Rust,这是一篇Rust代码风暴

Rust 是一门系统编程语言,专注于安全,尤其是并发安全。它支持函数式和命令式以及泛型等编程范式的多范式语言,且 TensorFlow 等深度学习框架也把它作为一个优秀的前端语言。

Rust 在语法上和 C、C++类似,都由花括弧限定代码块,并有相同的控制流关键字,但 Rust 设计者想要在保证性能的同时提供更好的内存安全。Rust 自 2016 年就已经开源了,在各种开发者调查中,它也总能获得「最受欢迎的语言」这一称赞,目前该开源项目已有 42.9K 的 Star 量。

机器之心的读者大多数都非常熟悉 Python,而 Rust 就没那么熟悉了。在 Amos 最近的一篇博文中,他表示如果阅读他的作品,我们半个小时就能入门 Rust。因此在这篇文章中,我们将介绍该博文的主要内容,它并不关注于 1 个或几个关键概念,相反它希望通过代码块纵览 Rust 的各种特性,包括各种关键词与符号的意义。

在 HackNews 上,很多开发者表示这一份入门教程非常实用,Rust 的入门门槛本来就比较高,如果再介绍各种复杂的概念与特性,很容易出现「从入门到劝退」。因此这种从实例代码出发的教程,非常有意义。

半小时入门Rust,这是一篇Rust代码风暴

从变量说起

let 能绑定变量:

<code>let x; // declare "x"</code><code>x = 42; // assign 42 to "x"</code><code>let x = 42; // combined in one line</code>

可以使用 :来制定变量的数据类型,以及数据类型注释:

<code>let x: i32; // `i32` is a signed 32-bit integer</code><code>x = 42;</code><code>// there's i8, i16, i32, i64, i128</code><code>//    also u8, u16, u32, u64, u128 for unsigned</code><code>let x: i32 = 42; // combined in one line</code>

如果你声明一个变量并在初始化之前就调用它,编译器会报错:

<code>let x;</code><code>foobar(x); // error: borrow of possibly-uninitialized variable: `x`</code><code>x = 42;</code>

然而,这样做完全没问题:

<code>let x;</code><code>x = 42;</code><code>foobar(x); // the type of `x` will be inferred from here</code>

下划线表示特殊的命名,或者更确切地说是「缺失的命名」,它和 Python 的用法有点像:

<code>// this does *nothing* because 42 is a constant</code><code>let _ = 42;</code><code>// this calls `get_thing` but throws away its result</code><code>let _ = get_thing();</code>

以下划线开头的命名是常规命名,只是编译器不会警告它们未被使用:

<code>// we may use `_x` eventually, but our code is a work-in-progress</code><code>// and we just wanted to get rid of a compiler warning for now.</code><code>let _x = 42;</code>

相同命名的单独绑定是可行的,第一次绑定的变量会取消:

<code>let x = 13;</code><code>let x = x + 3;</code><code>// using `x` after that line only refers to the second `x`,</code><code>// the first `x` no longer exists.</code>

Rust 有元组类型,可以将其看作是「不同数据类型值的定长集合」。

<code>let pair = ('a', 17);</code><code>pair.0; // this is 'a'</code><code>pair.1; // this is 17</code>

如果真的想配置 pair 的数据类型,可以这么写:

let pair: (char, i32) = ('a', 17);

元组在赋值时可以被拆解,这意味着它们被分解成各个字段:

<code>let (some_char, some_int) = ('a', 17);</code><code>// now, `some_char` is 'a', and `some_int` is 17</code>

当一个函数返还一个元组时会非常有用:

let (left, right) = slice.split_at(middle);

当然,在解构一个元组时,可以只分离它的一部分:

let (_, right) = slice.split_at(middle);

分号表示语句的结尾:

<code>let x = 3;</code><code>let y = 5;</code><code>let z = y + x;</code>

不加分号意味着语句可以跨多行:

<code>let x = vec![1, 2, 3, 4, 5, 6, 7, 8]</code><code>    .iter()</code><code>    .map(|x| x + 3)</code><code>    .fold(0, |x, y| x + y);</code>

函数来了

fn 声明一个函数。下面是一个空函数:

<code>fn greet() {</code><code>    println!("Hi there!");</code><code>}</code>

这是一个返还 32 位带符号整数值的函数。箭头表示返还类型:

<code>fn fair_dice_roll() -> i32 {</code><code>    4</code><code>}</code>

花括号表示了一个代码块,且拥有其自己的作用域:

<code>// This prints "in", then "out"</code><code>fn main() {</code><code>    let x = "out";</code><code>    {</code><code>        // this is a different `x`</code><code>        let x = "in";</code><code>        println!(x);</code><code>    }</code><code>    println!(x);</code><code>}</code>

代码块也是表示式,表示其计算为一个值。

<code>// this:</code><code>let x = 42;</code><code>// is equivalent to this:</code><code>let x = { 42 };</code>

在一个代码块中,可以有多个语句:

<code>let x = {</code><code>    let y = 1; // first statement</code><code>    let z = 2; // second statement</code><code>    y + z // this is the *tail* - what the whole block will evaluate to</code><code>};</code>

这也是为什么「省略函数末尾的分号」等同于加上了 Retrun,这些都是等价的:

<code>fn fair_dice_roll() -> i32 {</code><code>    return 4;</code><code>}</code><code>fn fair_dice_roll() -> i32 {</code><code>    4</code><code>}</code>

if 条件语句也是表达式:

<code>fn fair_dice_roll() -> i32 {</code><code>    if feeling_lucky {</code><code>        6</code><code>    } else {</code><code>        4</code><code>    }</code><code>}</code>

match 匹配器也是一个表达式:

<code>fn fair_dice_roll() -> i32 {</code><code>    match feeling_lucky {</code><code>        true => 6,</code><code>        false => 4,</code><code>    }</code><code>}</code>

Dots 通常用于访问某个对象的字段:

<code>let a = (10, 20);</code><code>a.0; // this is 10</code><code>let amos = get_some_struct();</code><code>amos.nickname; // this is "fasterthanlime"</code>

或者调用对象的方法:

<code>let nick = "fasterthanlime";</code><code>nick.len(); // this is 14</code>

双冒号与此类似,但可对命名空间进行操作。在此举例中,std 是一个 crate (~ a library),cmp 是一个 module(~ a source file),以及 min 是个函数:

let least = std::cmp::min(3, 8); // this is 3

use 指令可用于从其他命名空间中「引入范围」命名:

<code>use std::cmp::min;</code><code>let least = min(7, 1); // this is 1</code>

在 use 指令中,花括号还有另一个含义:「globs」,因此可以同时导入 min 以及 max:

<code>// this works:</code><code>use std::cmp::min;</code><code>use std::cmp::max;</code><code>// this also works:</code><code>use std::cmp::{min, max};</code><code>// this also works!</code><code>use std::{cmp::min, cmp::max};</code>

通配符(*)允许从命名空间导入符号:

<code>// this brings `min` and `max` in scope, and many other things</code><code>use std::cmp::*;</code>

Types 也是命名空间和方法,它可以作为常规函数调用:

<code>let x = "amos".len(); // this is 4</code><code>let x = str::len("amos"); // this is also 4</code>

str 是一个基元数据类型,但在默认情况下,许多非基元数据类型也在作用域中。

<code>// `Vec` is a regular struct, not a primitive type</code><code>let v = Vec::new();</code><code>// this is exactly the same code, but with the *full* path to `Vec`</code><code>let v = std::vec::Vec::new()</code>

至于为什么可行,因为 Rust 在每个模块的开头都插入了:

use std::prelude::v1::*;

再说说结构体

使用 struct 关键字声明结构体:

<code>struct Vec2 {</code><code>    x: f64, // 64-bit floating point, aka "double precision"</code><code>    y: f64,</code><code>}</code>

可以使用结构语句初始化:

<code>let v1 = Vec2 { x: 1.0, y: 3.0 };</code><code>let v2 = Vec2 { y: 2.0, x: 4.0 };</code><code>// the order does not matter, only the names do</code>

有一个快捷方式可以从另一个结构体初始化本结构体的其余字段:

<code>let v3 = Vec2 {</code><code>    x: 14.0,</code><code>    ..v2</code><code>};</code>

这就是所谓的「结构体更新语法」只能发生在最后一个位置,不能在其后面再跟一个逗号。

注意其余字段可以表示所有字段:

let v4 = Vec2 { ..v3 };

结构体与元组一样,可以被解构。例如一个有效的 let 模式:

<code>let (left, right) = slice.split_at(middle);</code><code>let v = Vec2 { x: 3.0, y: 6.0 };</code><code>let Vec2 { x, y } = v;</code><code>// `x` is now 3.0, `y` is now `6.0`</code><code>let Vec2 { x, .. } = v;</code><code>// this throws away `v.y`</code>

让 let 模式在 if 里可以作为条件:

<code>struct Number {</code><code>    odd: bool,</code><code>    value: i32,</code><code>}</code><code>fn main() {</code><code>    let one = Number { odd: true, value: 1 };</code><code>    let two = Number { odd: false, value: 2 };</code><code>    print_number(one);</code><code>    print_number(two);</code><code>}</code><code>fn print_number(n: Number) {</code><code>    if let Number { odd: true, value } = n {</code><code>        println!("Odd number: {}", value);</code><code>    } else if let Number { odd: false, value } = n {</code><code>        println!("Even number: {}", value);</code><code>    }</code><code>}</code><code>// this prints:</code><code>// Odd number: 1</code><code>// Even number: 2</code>

多分支的 match 也是条件模式,就像 if let:

<code>fn print_number(n: Number) {</code><code>    match n {</code><code>        Number { odd: true, value } => println!("Odd number: {}", value),</code><code>        Number { odd: false, value } => println!("Even number: {}", value),</code><code>    }</code><code>}</code><code>// this prints the same as before</code>

match 必须是囊括所有情况的的:至少需要匹配一个条件分支。

<code>fn print_number(n: Number) {</code><code>    match n {</code><code>        Number { value: 1, .. } => println!("One"),</code><code>        Number { value: 2, .. } => println!("Two"),</code><code>        Number { value, .. } => println!("{}", value),</code><code>        // if that last arm didn't exist, we would get a compile-time error</code><code>    }</code><code>}</code>

如果非常难实现, 那么可以作用一个“包罗万象”的模式:

<code>fn print_number(n: Number) {</code><code>    match n.value {</code><code>        1 => println!("One"),</code><code>        2 => println!("Two"),</code><code>        _ => println!("{}", n.value),</code><code>    }</code><code>}</code>

Type 别名

我们可以使用 type 关键字声明另一类型的别名,然后就可以像使用一个真正的类型一样使用这种类型。例如定义 Name 这种数据类型为字符串,后面就可以直接使用 Name 这种类型了。

你可以在方法中声明不同的数据类型:

<code>struct Number {</code><code>    odd: bool,</code><code>    value: i32,</code><code>}</code><code>impl Number {</code><code>    fn is_strictly_positive(self) -> bool {</code><code>        self.value > 0</code><code>    }</code><code>}</code>

然后就如同往常那样使用:

<code>fn main() {</code><code>    let minus_two = Number {</code><code>        odd: false,</code><code>        value: -2,</code><code>    };</code><code>    println!("positive? {}", minus_two.is_strictly_positive());</code><code>    // this prints "positive? false"</code><code>}</code>

默认情况下,声明变量后它就就是不可变的,如下 odd 不能被重新赋值:

<code>fn main() {</code><code>    let n = Number {</code><code>        odd: true,</code><code>        value: 17,</code><code>    };</code><code>    n.odd = false; // error: cannot assign to `n.odd`,</code><code>                   // as `n` is not declared to be mutable</code><code>}</code>

不可变的变量声明,其内部也是不可变的,它也不能重新分配值:

<code>fn main() {</code><code>    let n = Number {</code><code>        odd: true,</code><code>        value: 17,</code><code>    };</code><code>    n = Number {</code><code>        odd: false,</code><code>        value: 22,</code><code>    }; // error: cannot assign twice to immutable variable `n`</code><code>}</code>

mut 可以使变量声明变为可变的:

<code>fn main() {</code><code>    let mut n = Number {</code><code>        odd: true,</code><code>        value: 17,</code><code>    }</code><code>    n.value = 19; // all good</code><code>}</code>

Traits 描述的是多种数据类型的共同点:

<code>trait Signed {</code><code>    fn is_strictly_negative(self) -> bool;</code><code>}</code>

我们可以在我们定义的 Type 类型中定义 Traits:

<code>impl Signed for Number {</code><code>    fn is_strictly_negative(self) -> bool {</code><code>        self.value < 0</code><code>    }</code><code>}</code><code>fn main() {</code><code>    let n = Number { odd: false, value: -44 };</code><code>    println!("{}", n.is_strictly_negative()); // prints "true"</code><code>}</code>

外部类型(foreign type)中定义的 Trait:

<code>impl Signed for i32 {</code><code>    fn is_strictly_negative(self) -> bool {</code><code>        self < 0</code><code>    }</code><code>}</code><code>fn main() {</code><code>    let n: i32 = -44;</code><code>    println!("{}", n.is_strictly_negative()); // prints "true"</code><code>}</code>

impl 模块通常会带有一个 Type 类型,所以在模块内,Self 就表示该类型:

<code>impl std::ops::Neg for Number {</code><code>    type Output = Self;</code><code>    fn neg(self) -> Self {</code><code>        Self {</code><code>            value: -self.value,</code><code>            odd: self.odd,</code><code>        }        </code><code>    }</code><code>}</code>

有一些traits只是作为标记,它们并不是说 Type 类型实现了某些方法,它只是表明某些东西能通过Type类型完成。例如,i32 实现了Copy,那么以下代码就是可行的:

<code>fn main() {</code><code>    let a: i32 = 15;</code><code>    let b = a; // `a` is copied</code><code>    let c = a; // `a` is copied again</code><code>}</code>

下面的代码也是能运行的:

<code>fn print_i32(x: i32) {</code><code>    println!("x = {}", x);</code><code>}</code><code>fn main() {</code><code>    let a: i32 = 15;</code><code>    print_i32(a); // `a` is copied</code><code>    print_i32(a); // `a` is copied again</code><code>}</code>

但是 Number 的结构体并不能用于 Copy,所以下面的代码会报错:

<code>fn main() {</code><code>    let n = Number { odd: true, value: 51 };</code><code>    let m = n; // `n` is moved into `m`</code><code>    let o = n; // error: use of moved value: `n`</code><code>}</code>

同样下面的代码也不会 Work:

<code>fn print_number(n: Number) {</code><code>    println!("{} number {}", if n.odd { "odd" } else { "even" }, n.value);</code><code>}</code><code>fn main() {</code><code>    let n = Number { odd: true, value: 51 };</code><code>    print_number(n); // `n` is moved</code><code>    print_number(n); // error: use of moved value: `n`</code><code>}</code>

但是如果print_number有一个不可变reference,那么 Copy 就是可行的:

<code>fn print_number(n: &Number) {</code><code>    println!("{} number {}", if n.odd { "odd" } else { "even" }, n.value);</code><code>}</code><code>fn main() {</code><code>    let n = Number { odd: true, value: 51 };</code><code>    print_number(&n); // `n` is borrowed for the time of the call</code><code>    print_number(&n); // `n` is borrowed again</code><code>}</code>

如果函数采用了可变reference,那也是可行的,只不过需要在变量声明中带上 mut。

<code>fn invert(n: &mut Number) {</code><code>    n.value = -n.value;</code><code>}</code><code>fn print_number(n: &Number) {</code><code>    println!("{} number {}", if n.odd { "odd" } else { "even" }, n.value);</code><code>}</code><code>fn main() {</code><code>    // this time, `n` is mutable</code><code>    let mut n = Number { odd: true, value: 51 };</code><code>    print_number(&n);</code><code>    invert(&mut n); // `n is borrowed mutably - everything is explicit</code><code>    print_number(&n);</code><code>}</code>

Copy 这类标记型的traits并不带有方法:

<code>// note: `Copy` requires that `Clone` is implemented too</code><code>impl std::clone::Clone for Number {</code><code>    fn clone(&self) -> Self {</code><code>        Self { ..*self }</code><code>    }</code><code>}</code><code>impl std::marker::Copy for Number {}</code>

现在 Clone 仍然可以用于:

<code>fn main() {</code><code>    let n = Number { odd: true, value: 51 };</code><code>    let m = n.clone();</code><code>    let o = n.clone();</code><code>}</code>

但是Number的值将不会再移除:

<code>fn main() {</code><code>    let n = Number { odd: true, value: 51 };</code><code>    let m = n; // `m` is a copy of `n`</code><code>    let o = n; // same. `n` is neither moved nor borrowed.</code><code>}</code>

有一些traits很常见,它们可以通过使用derive 属性自动实现:

<code>#[derive(Clone, Copy)]</code><code>struct Number {</code><code>    odd: bool,</code><code>    value: i32,</code><code>}</code><code>// this expands to `impl Clone for Number` and `impl Copy for Number` blocks.</code>

看上去,整篇教程都在使用大量代码解释 Rust 的各种语句与用法。可能我们会感觉博客结构不是太明确,但是实例驱动的代码学习确实更加高效。尤其是对于那些有一些编程基础的同学,他们可以快速抓住 Rust 语言的特点与逻辑。

最后,这篇文章并没有展示博客所有的内容,如果读者想真正入门 Rust 语言,推荐可以查阅原博客。

原文地址:https://fasterthanli.me/blog/2020/a-half-hour-to-learn-rust/


以上所述就是小编给大家介绍的《半小时入门Rust,这是一篇Rust代码风暴》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

scikit learn机器学习

scikit learn机器学习

黄永昌 / 机械工业出版社 / 2018-3-1 / CNY 59.00

本书通过通俗易懂的语言、丰富的图示和生动的实例,拨开了笼罩在机器学习上方复杂的数学“乌云”,让读者以较低的代价和门槛轻松入门机器学习。本书共分为11章,主要介绍了在Python环境下学习scikit-learn机器学习框架的相关知识。本书涵盖的主要内容有机器学习概述、Python机器学习软件包、机器学习理论基础、k-近邻算法、线性回归算法、逻辑回归算法、决策树、支持向量机、朴素贝叶斯算法、PCA ......一起来看看 《scikit learn机器学习》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

随机密码生成器
随机密码生成器

多种字符组合密码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具