3.Rust通用编程概念

这期我们将会介绍一些通用的编程概念(也就是通常编程语言都有的东西),如数据类型、函数、控制流等

一、变量和数据类型
1.Rust的关键字

首先我们先介绍一下Rust的关键字,在变量命名时以免冲撞~

[Keywords Currently in Use]

The following keywords currently have the functionality described.

  • as - perform primitive casting, disambiguate the specific trait containing an item, or rename items in use and extern crate statements
  • async - return a Future instead of blocking the current thread
  • await - suspend execution until the result of a Future is ready
  • break - exit a loop immediately
  • const - define constant items or constant raw pointers
  • continue - continue to the next loop iteration
  • crate - link an external crate or a macro variable representing the crate in which the macro is defined
  • dyn - dynamic dispatch to a trait object
  • else - fallback for if and if let control flow constructs
  • enum - define an enumeration
  • extern - link an external crate, function, or variable
  • false - Boolean false literal
  • fn - define a function or the function pointer type
  • for - loop over items from an iterator, implement a trait, or specify a higher-ranked lifetime
  • if - branch based on the result of a conditional expression
  • impl - implement inherent or trait functionality
  • in - part of for loop syntax
  • let - bind a variable
  • loop - loop unconditionally
  • match - match a value to patterns
  • mod - define a module
  • move - make a closure take ownership of all its captures
  • mut - denote mutability in references, raw pointers, or pattern bindings
  • pub - denote public visibility in struct fields, impl blocks, or modules
  • ref - bind by reference
  • return - return from function
  • Self - a type alias for the type implementing a trait
  • self - method subject or current module
  • static - global variable or lifetime lasting the entire program execution
  • struct - define a structure
  • super - parent module of the current module
  • trait - define a trait
  • true - Boolean true literal
  • type - define a type alias or associated type
  • unsafe - denote unsafe code, functions, traits, or implementations
  • use - bring symbols into scope
  • where - denote clauses that constrain a type
  • while - loop conditionally based on the result of an expression
[Keywords Reserved for Future Use]

The following keywords do not have any functionality but are reserved by Rust for potential future use.

  • abstract
  • become
  • box
  • do
  • final
  • macro
  • override
  • priv
  • try
  • typeof
  • unsized
  • virtual
  • yield
2.变量声明

上期我们看到有一个mut变量的声明,那么没有mut关键字呢

fn main() {
    let a = 21;
    println!("a: {} ", a);
}

能够正确打印a的值,而下面这段代码

fn main() {
    let a = 21;
    println!("a: {} ", a);
    a = 33;
    println!("a: {} ", a);
}

出现错误提示:

3 |     println!("a: {} ", a);
4 |     a = 33;
  |     ^^^^^^ cannot assign twice to immutable variable

在Rust中,没有加mut的变量,是不可变的哟,不能再次赋值。我们可以称之为不可变变量,将有mut关键字的变量称为可变变量

那大家就会有疑问了,那这和常量又有什么区别呢?

fn main() {
    const THIS_YEAR : u32 = 2020;
    println!("{} ",THIS_YEAR);
}

在Rust中使用const关键字定义常量。看下面代码

fn main() {
    let a : u32;
    a = 33;
}

非可变变量可以在定义时不初始化,而后再赋值。那常量呢?

fn main() {
    const THIS_YEAR : u32;
    THIS_YEAR = 2000;
    println!("{} ",THIS_YEAR);
}

在这里插入图片描述
编译器提示我们,常量必须拥有一个value

常量必须初始化,且右边必须是编译时就确定的结果,而不能时运行时。

3.变量遮蔽(shadowing)

什么是变量遮蔽呢,前面我们提到不可变变量是无法重新赋值的,请看下面代码。

fn main() {
    let var = 59;
    println!("{}", var);
    let var = var + 1;
    let mut var = var + 1;
    println!("{}", var);
    let var = "白纸";
    println!("{}", var);
    let var = var.len();
    println!("{}", var);
}

运行结果:

59
60
61
白纸
6

咦,这里var怎么一直在变呢?其实不然,这里我们其实是使用let关键字将var变量重新定义了,而不是重新赋值,相当于直接覆盖了。

那么这样做到底有什么作用呢,其实还是很有用的。比如,我们需要在同一个函数内部把一个变量转换为另一个类型的变量,但又不想给它们起不同的名字。再比如,在同一个函数内部,需要修改一个变量绑定的可变性。例如,我们对一个可变数组执行初始化,希望此时它是可读写的,但是初始化完成后,我们希望它是只读的。

use std::vec;

fn main() {
    let mut vec = Vec::new(); //此时我们需要vec是可读的,因为我们还需要插入数据
    vec.push(1);
    vec.push(2);
    vec.push(3);
    let vec = vec;	//此时前面的vec已经被遮蔽,无法再访问
    for i in &vec {
        println!("{}", i);
    }
}

运行结果:

1
2
3

在上面程序中,我们希望vec再初始化以后就不再是可写的了,因此采用变量遮蔽的办法实现。

还有一些别的例子,就先举着一个例子吧,应该还是很好理解的,总之就是“严谨!”,能够节省开销。

3.基本数据类型

bool

和其他语言差不多,就不赘述了

char

字符类型,可以描述任何一个符合unicode标准的字符值,通常用单引号包围

let c1 = '\n';
let love = '❥';

由于char类型设计的目的是描述unicode字符,因此它所占据的内存空间是4个字节。

但是Rust同样提供了单字节字符字面量来表示ASCII字符,我们可以使用一个字母b 在字符或者字符串前面,代表这个字面量存储在u8 类型数组中,这样占用空间比char 型数组要小一些。

fn main() {
    let c1 = b's';
    let s1 = b"shuaishuai";
}

这里顺便介绍一下Rust的类型推导,IDE帮我们标识了出来,但是直接复制代码是看不出来的。截图一下

这里呢,我们并没有敲变量类型,而是输入右边的值以后自动显示了,非常滴方便!当然我们也可以自己显示地定义数据的类型。

整数类型

Rust中的整数分很多类型,具体见下表,

Length Signed Unsigned
8-bit i8 u8
16-bit i16 u16
32-bit i32 u32
64-bit i64 u64
128-bit i128 u128
arch isize usize

注:有符号和无符号不仅仅表现在含义上,还表现在内存空间中bit表达的意义。有符号类型的最高位是符号位,无符号类型的最高位和其他位一样表示数字的大小。例如,对于8位的数据来说,如果存的是无符号数,表示范围则是0255,有符号数则表示-128127,应该是很好理解的。

我们c/c++程序员都知道,整数溢出是一个需要重视的问题,处理不好很有可能会导致未知的bug。那么Rust是怎么处理整数溢出的呢?

默认情况下,在debug 模式下编译器会自动插入整数溢出检查, 一旦发生溢出, 则会引发panic ;在release 模式下,不检查整数溢出,而是采用自动舍弃高位的方式。

那大家知道c是怎么处理整数溢出的吗?后面如果建群的话可以一起探讨探讨。

浮点类型

f32f64两种类型,默认类型是f64,那大家知道为什么默认是f64而不是f32吗?哈哈

浮点数可能的状态:

enum FpCategory {
	Nan,
	Infinite,
	Zero,
	Subnormal,
	Normal,
}

Zero:表示0值,正儿八经的0.

Normal:正常的浮点类型。

Subnormal:以后有机会一起讨论吧,不想说这个了。

Nan:不是数字,0 / 0.

Infinite:无穷大,1 / 0.

4.复合数据类型

这里我们先只讲元组类型,至于结构体,枚举和字符串以后再讲。

tuple:元组

废话不多说,直接上代码,用心感受就好了

这里我们采用截图的方式,这样可以清楚的看到自动类型推导。

运行结果:

就不要解释了,相信大家已经深刻体会到了。

array:数组

继续体会:

fn main() {
    let array1 = [1, 2, 3, 4, 5, 6];
    println!("first: {}", array1[0]);
}

数组也就那么回事。

关于数组下标越界:Rust在编译时不会报错,在运行时会报错。

还有一些别的内容,多维数组、数组切片、大小比较等内容以后再做专门介绍。

二、函数

Rust的函数需要使用fn关键字开头。

fn main() {
    print_input_param(20);
}

fn print_input_param(x : i32) {
    println!("input_param: {}", x);
}

运行结果:

input_param: 20

上面是一个简单的单参数无返回值函数,很好理解,下面将演示一个多参数带返回值的函数

fn main() {
    println!("result: {}", add1((1, 2)));
}

fn add1((x, y) : (i32, i32)) -> i32 {
    return x + y;
}

运行结果:

result: 3

使用return语句返回了函数的返回值,返回值类型是i32。

Rust也可以不通过Rust语句返回返回值。

fn main() {
    println!("result: {}", add2((1, 2)));
}

fn add2((x, y) : (i32, i32)) -> i32 {
    x + y
}

注意没有分号哟。

上面的输入参数说到底其实就是个元组,因此也可以使用元组作为输入参数

fn main() {
    let tup1 = (1, 2);
    println!("result: {}", add3(tup1));
}

fn add3(t : (i32, i32)) -> i32 {
    t.0 + t.1
}

函数在Rust中是头等公民(first class value),可以被复制到一个值中

fn main(){
    let func = add1;
    println!("func: {}", func((2, 3)));
}

fn add1((x, y) : (i32, i32)) -> i32 {
    return x + y;
}

运行结果:

func: 5

Rust中的每一个函数都有自己的类型

fn main(){
    let mut func = add1;
    func = add2;
}

fn add1((x, y) : (i32, i32)) -> i32 {
    return x + y;
}
fn add2((x, y) : (i32, i32)) -> i32 {
    return x + y + 2;
}

编译报错:

error[E0308]: mismatched types
 --> src\main.rs:3:12
  |
3 |     func = add2;
  |            ^^^^ expected fn item, found a different fn item
  |
  = note: expected type `fn((i32, i32)) -> i32 {add1}`
             found type `fn((i32, i32)) -> i32 {add2}`

尽管参数类型一样,返回值类型一样,但这两个函数仍然是不同类型的函数。

fn main(){
    let mut func = add1;
    let mut func1 = add1;
    func = func1;
    println!("func: {}", func((3, 9)));
}

fn add1((x, y) : (i32, i32)) -> i32 {
    return x + y;
}

正常运行,你可能觉得这个程序多余,但是我不觉得

上上个程序中,怎么才能正常运行呢,方案是让func的类型不是add1而是通用的fn类型就可以了。

方法一:as

fn main(){
    let mut func = add1 as fn((i32, i32)) -> i32;
    println!("add1: {}", func((1, 2)));
    func = add2;
    println!("add2: {}", func((1, 2)));
}

fn add1((x, y) : (i32, i32)) -> i32 {
    return x + y;
}
fn add2((x, y) : (i32, i32)) -> i32 {
    return x + y + 2;
}

运行结果:

add1: 3
add2: 5

方法二:通过显示类型标记

fn main(){
    let mut func : fn((i32, i32)) ->i32 = add1;
    println!("add1: {}", func((1, 2)));
    func = add2;
    println!("add2: {}", func((1, 2)));
}

fn add1((x, y) : (i32, i32)) -> i32 {
    return x + y;
}
fn add2((x, y) : (i32, i32)) -> i32 {
    return x + y + 2;
}

运行结果:

add1: 3
add2: 5

当然,这仅仅适用于能够转换为同一个fn类型的函数,至于为什么我要解释这一下,因为我觉得不多余。

三、语句和表达式

statement and expression

1.表达式

运算表达式

和其他语言相差不大,没啥好说的

赋值表达式

这里涉及到所有权的问题,后面会专门讲一章

语句块表达式

就是{}包起来

2.if-else

和其他语言大同小异吧

3.loop

死循环

continue break

4.while

和其他语言差不多

5.for

涉及到迭代器,后面再讲哈

欢迎关注我的微信公众号:Rust编程之路,我将持续同步更新
在这里插入图片描述

发布了27 篇原创文章 · 获赞 70 · 访问量 9397

猜你喜欢

转载自blog.csdn.net/qq_23905237/article/details/105272980