阅读 154

Rust的一般概念(rust是解释型语言吗)

变量和可变性

变量默认是不可变(immutalbe)的。刚开始学习Rust的人可能不太习惯,但是变量默认不可变能够提升程序的安全性且更容易做到并发。为什么Rust鼓励你使用不可变的变量呢?

当一个变量是不可变的时候,一旦某个值绑定到这个变量了,你就不能再改变这个值了。我们新建一个工程来测试一下:

cargo new varibales --bin

main.rs内容为:

fn main() {     let x = 5;     println!("x = {}", x);     x = 6; // 报错!error[E0384]     println!("x = {}", x); } 复制代码

运行cargo run试编译一下,在 x = 6; 那一行会报错!

error[E0384]: cannot assign twice to immutable variable x

意思是你不能对一个不可变的变量x进行二次赋值!我们在let x = 5;的时候并没有指定x是immutable的,为什么Rust会这么提示呢?因为Rust默认会给变量添加不可变的属性,只要你没有给变量添加可变(mut)修饰,它就是不可变的。

如果要让变量变成可变的,需要在let后面添加mut关键字。

fn main() {     let mut x = 5;     println!("x = {}", x);     x = 6;     println!("x = {}", x); } 复制代码

变量和常量

既然有变量就会有常量,常量和immutable的变量很像,但有一些不同:

  1. 你不能用mut来修饰常量,常量不仅仅叫做默认不可变,它是永远不能变!

  2. 常量用const关键字来定义,而不是let,常量定义时必须指明类型

fn main() {     let mut x = 5;     println!("x = {}", x);     x = 6;     println!("x = {}", x);     let y = 7;     println!("y = {}", y);     const HUNDRED: u32 = 100;    println!("HUNDRED = {}", HUNDRED); } 复制代码

  1. 常量可以在任何地方定义,包括全局的!let只能在函数中使用,如果我们将let y = 7移动到main函数外面的,编译会报错!

let y = 7; // 报错!error: expected item, found keyword `let` fn main() {     ... 复制代码

  1. 常量不能通过函数调用赋值,只能使用常量表达式赋值;

const HUNDRED: u32 = 100 + 6; // 正确 fn main() {     ... 复制代码

变量遮蔽(Shadowing)

当你定义了和前面变量一样的名字的时候,新的变量会将之前旧的变量名盖住。

fn main() {     let x = 1;     let x = x + 1;     let x = x * 3;     println!("x = {}", x); } 复制代码

运行结果:x = 6

变量遮蔽和可变变量的比较:

  1. 变量遮蔽每次都需要关键字let,是新建一个变量!可变变量只使用过一次let,并需要用mut来修饰变量,第二次赋值不是新建变量;

  2. 变量遮蔽可以改变值的类型

let spaces = " "; let spaces = spaces.len(); 复制代码

第一个spaces的类型是字符串类型,第二个spaces是整数类型。也可以这样理解,第二个整数类型的变量spaces刚好用了和第一个字符串类型的变量相同的名字!

而如果我们用mut来修饰变量,然后赋予不同类型的值的话,就会报类型不匹配的错误!

let mut spaces = " "; spaces = spaces.len(); // error[E0308]: mismatched types                        // expected `&str`, found `usize` 复制代码

数据类型

在Rust中每一个值都有一个确定的数据类型!数据类型包含有2个大的子集:数值型和复合类型。

Rust是一门静态类型语言,静态类型语言意味着Rust必须在编译期间就确定所有的变量类型!我们在很多地方都会碰到将字符串转换成数值的需求,因为数值类型有很多种类,如果编译器在编译期间无法确定最终数值的类型的话,就会报错,它要求开发者指定好!比如:

let guess = "42".trim().parse().expect("请输入一个整型数!"); 复制代码

报错:error[E0282]: type annotations needed;

consider giving guess a type  就是要给guess指定一个类型,比如 guess: u32

数值类型(Scalar Types)

Rust有4种基本的数值类型:整型,浮点型,布尔型和字符型。每个数值类型代表一个值。我们稍微看看。

整型

整型没有小数部分,它可以分为有符号数和无符号数。有符号数的整型用i开头,无符号数的整型用u开头。 Rust内建支持的整型类型

长度有符号无符号
8位i8u8
16位i16u16
32位i32u32
64位i64u64
架构相关isizeusize

isize和usize是与运行程序的CPU架构相关的:在32位机器上是64位长度,在64位机器上是64位长度。如果你不确定用哪一个整型类型,那么就用Rust默认的,为i32,++使用这个类型进行运算时是最快的,即使在64位计算机上也是++。

在Rust中有多种整型字面常量的书写方式。其中下划线_仅用来便于开发者阅读,不对数字的大小构成影响!

数字字面常量举例
十进制98_222
十六进制0xff
八进制0o77
二进制0b1111_0000
字节b'A'

注意:

b‘A'会打印成65,’A'会打印成A。 0x表示的是十六进制,0o表示的是八进制,0b表示的是二进制,在数字0后面分别是小写字母x、o和b,不能是大写的

测试一下:

fn main() {     let x = 20_0000;     println!("x is {}", x);     let x = 0x20_0000;     println!("x is {}", x);     let x = 0o77;     println!("x is {}", x);     let x = 0b1111_0000;     println!("x is {}", x);     let x = b'A';     println!("x is {}", x);     let x = 'A';     println!("x is {}", x); } 复制代码

运行结果:

x is 200000 x is 2097152 x is 63 x is 240 x is 65 x is A 复制代码

浮点类型

Rust支持2种基本的浮点类型:单精度f32和双精度f64。默认的浮点类型是f64,因为现代的计算机性能都很强劲,计算64位的速度与32位差不多,前者的的精度更高,适用范围更广。

let x = 2.0;    // f64 let y: f32 = 3.0; // f32 复制代码

布尔类型

fn main() {     let t = true;     let f: bool = false; // with explicit type annotation } 复制代码

字符类型

字符用单引号包围起来,字符串用双引号

fn main() {     let c = 'z';     let z = 'Ƶ';     let heart_eyed_cat = '????';     let zhong = '中';     println!("heart_eyed_cat is {}", heart_eyed_cat);     println!("zhong is {}", zhong);   } 复制代码

输出:

heart_eyed_cat is ???? zhong is 中 复制代码

Rust中的字符类型不是单纯的ASCII字符编码,而是Unicode编码!所以我们可以看到上面能够打印出中文和Emoji表情。Unicode编码值的范围是U+0000 ~ U+D7FF,以及U+E000 ~ U+10FFFF。

因为Rust的字符类型与我们通常了解的很不一样,要特别注意!

复合类型

复合类型可以将多个值组织到一个类型中,Rust支持2种基本的复合类型:元组(tuples)和数组(arrays)

元组类型(元/圆括号())

元组可以将多个不同类型的值放在一个集合里。 创建元组时需要用圆括号括起来,里面的每个值用逗号隔开。你可以为每个元素指定好类型,也可以由Rust帮你推断。

let tup: (i32, f64, u8) = (2, 3.0, 250); // 正确 let tup = (2, 3.0, 250); // 正确,自动推断类型 let tup: (i32, f64) = (2, 3.0, 250); // 错误 error[E0308]: mismatched types:             // expected a tuple with 2 elements, found one with 3 elements 复制代码

前两句表达式都创建了tup元组,元组中有三个值,分别为2, 3.0, 250,前者指定了值类型,后者由Rust自动推断三个值的类型。

如何读取元组中的数据呢?

fn main() {     let tup: (i32, f64, u8) = (2, 3.0, 250);     let mut tup = (2, 4.0, 250);     let (x, mut y, z) = tup; // 解元组:将一个元组分解成几个部分     println!("y is {}", y);     println!("tup.0 is {}", tup.0); // 点运算     tup.2 = 200;     y = 5.0;     println!("y is {}", y);     println!("z is {}", z); } 复制代码

运行结果:

y is 4 tup.0 is 2 y is 5 z is 250 复制代码

读取元组数据的方法:

  1. 将元组赋值给另外一个由变量组成的元组,然后用元组中的变量获得对应的值!

  2. 使用.(点)运算符和序号的方式获取,序号从0开始。

数组类型(方括号[])

另一个将多个值放在一个集合里的方法是数组,和元组不同,数组中的元素只能一种!Rust的数组的长度是固定的,一旦定义就不能再修改了!创建数组时需要用方括号括起来,里面的每个值用逗号隔开

let a = [1, 2, 3, 4, 5]; 复制代码

如何访问数组元素?方括号中加下标,下标从0开始。

    let a = [1, 2, 3, 4, 5];     println!("a[0] is {}", a[0]); 复制代码

如果我们访问不存在的序号呢?我们知道Rust的数组中的长度是固定的,所以猜想Rust会在编译时报错吧?是的!

println!("a[0] is {}", a[5]); // 报错 index out of bounds:                                // the length is 5 but the index is 5 复制代码

Rust在访问元素的时候会看序号是否超出范围了,超出的话就会果断panic退出了。这样的处理让程序变得更加安全,对比一些语言不做比较的话就会访问到不可用的内存,那么程序当时可能没有错误退出,在过一段时间之后才表现出来,问题追查的时候就比较难以定位到原因。

++标准库中提供的vector和array具有相似的功能++,都是同一种类型,但是vector不固定长度,灵活性要比array大得多,如果你不知道在这两者间如何选择,就选择vector使用。 下面这种情况用array很合适,如月份的定义、星期的定义,都是固定长度和同一种类型的。

let months = ["January", "February", "March", "April", "May", "June", "July",  "August", "September", "October", "November", "December"]; 复制代码

函数

函数在Rust中非常常见,像main函数就是其中一个非常重要的函数,它是整个应用程序的入口。Rust中的函数代码采用蛇形命名法编写,蛇形命名法是由全小写的字母和_下划线连接单词的方式。

fn main() {     println!("Hello, world!");     another_function(); } fn another_function() {     println!("Another function."); } 复制代码

函数以fn开头,后面跟着的是函数名。看上面的main函数可以调用到定义在其后面的another_function函数,说明在Rust中,被调用函数another_function的定义与调用者main函数书写先后顺序无关。

参数

函数的签名中,参数的类型是必须指定的!

fn main() {     another_function(5, 6); } fn another_function(x: i32, y: i32) {     println!("The value of x is: {}", x);     println!("The value of y is: {}", y); } 复制代码

函数中的语句Statement和表达式Expression

Rust中的函数应该由若干条语句和末尾的一条表达式组成(也可以没有表达式!)。和其它语言不一样,在Rust中对这两个概念做了区分。

语句是用来执行的指令,没有结果值。表达式更像是一个实物,则有一个实际的结果用于赋值。

let y = 6; // 语句 let x = (let y = 6); // 报错,因为let y = 6是语句,是没有返回值的 复制代码

语句没有返回值,所以你不能用语句作为一个值赋值给另外一个变量

let x = y = 6; // 在Rust中也是不成立的 复制代码

再看看下面的语句和表达式:

fn add(x: i32, y: i32) -> i32 {     return x + y; // 是一条语句 } fn minus(x: i32, y: i32) -> i32 {     x + y + 100 // 没有分号!为表达式,x+y+100的结果用于minus的返回值 } fn main() {     println!("result   add: {}", add(1, 2)); // 语句     println!("result minus: {}", minus(1, 2)) // 表达式,在函数的末尾了,这样写也可以正常编译 } 复制代码

控制流

fn main() {     let condition = true;     let number = if condition {         5     } else {         "six"     };     println!("The value of number is: {}", number); } 复制代码

会报错!因为if 是一个语句,所以它有返回值。但是两个分支的返回值不一样就不行!Rust是静态编译语言,无法处理这样类型不确定的情况!

循环

loop

嵌套的loop循环

fn main() {     'outer: loop {         println!("Entered the outer loop");         'inner: loop {             println!("Entered the inner loop");             // This would break only the inner loop             //break;             // This breaks the outer loop             break 'outer;         }         println!("This point will never be reached");     }     println!("Exited the outer loop"); } 复制代码

loop的返回值:

    let mut counter = 0;     let result = loop {         counter += 1;         if counter == 10 {             break counter * 2;         }     }; 复制代码

while

    while n > 0 {         println!("n is {}", n);         n -= 1;     } 复制代码

for

数据的iter方法

fn main() {     let a = [10, 20, 30, 40, 50];     for element in a.iter() {         println!("the value is: {}", element);     } } 复制代码

fn main() {     for number in (1..4).rev() {         println!("{}!", number);     }     println!("LIFTOFF!!!"); } 复制代码

for i in 1..=10 {     println!("i(include) is {}", i); }


作者:教头lily
链接:https://juejin.cn/post/7046761118101930021


文章分类
代码人生
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐