在上一节中我们了解了rust
最基础的使用,如何使用宏打印
,用了一个最简单的猜数游戏了解了rust
中的基本用法,今天我们还是从rust
中的基础知识中去学习rust
变量不可变性
当我用let
声明一个name
变量时,这个变量是不可变的
rust
fn main() {
let name = "Maic";
println!("Hello, {}!", name);
}
当我们重新对name
进行覆值时会报错
rust
fn main() {
let name = "Maic";
name = "2";
println!("Hello, {}!", name);
}
/**
2 | let name = "Maic";
| ------ expected due to this value
3 | name = '2';
| ^^^ expected `&str`, found `char`
**/
变量的可变性
如果我想让一个申明的变量可变,只需用mut
修饰即可,注意我们右侧的变量用双引号,不能用单引号。
rust
fn main() {
let mut name = "Tom";
println!("Hello, {}!", name);
name = "Jake";
// name = 'jake' error
println!("Hello, {}!", name);
}
/*
打印的结果是:
Hello, Tom!
Hello, Jake!
*/
常量
我是使用const
申明变量,且变量名是大驼峰命名,且申明这个变量,我们使用u32
类型
rust
fn main() {
const NAME_RESULT: u32 = 60 * 60;
println!("Hello, {}!", NAME_RESULT);
}
遮蔽变量
我们使用let
变量重复定义同一个变量名,这样会导致,第二个变量是前一个变量的引用
rust
fn main(){
// 1
let x:i32 = 5;
// 2
let x:i32 = x + 1;
{
// 3
let x:i32 = x + 3;
println!("hello x1={}",x);
}
// 4
println!("hello x2={}", x)
}
我们会发现我们重复申明了x
3次,打印的结果是x1=9 x2=6
。 在第2
行申明的x,引用的就是1
处申明的变量,在第3
行使用的是第二2
行申明的变量,{}
可以申明成一个独立的块作用域,而4
引用就是2
处申明的变量。
从结果上来看,2
把1
遮蔽了,3
把2
遮蔽了,但是4
引用的是2
处的变量
遮蔽变量类型不可变
在以上我们重复用let
定义了不可变量,我们发现遮蔽变量不可重新赋值且类型不可变
bash
fn main() {
let y:u32 = 5;
y = 10; // cannot mutate immutable variable `y`
println!("hello, {}", y)
}
要解决这个问题,我们只需把y
变成mut
即可,将该变量申明成一个可变量
bash
fn main() {
let mut y:u32 = 5;
y = 10;
println!("hello, {}", y)
}
那什么类型不可变,比如说:
bash
fn main() {
let str_name = "hello";
str_name = str_name.len(); // error expected `&str`, found `usize`
println!("hello, {}", str_name);
}
我们用let
申明了一个不可变变量,然后又重新赋值,并且获取了字符串
的长度
此时打印str_name
编辑就提示报错了 当我们使用不可变量想能重新赋值时,类型不可强制转换
rust
// ok
fn main() {
let str_name = "hello";
let str_name = str_name.len();
println!("hello length, {}", str_name); // 5
}
// error
fn main() {
let mut str_name = "hello";
str_name = str_name.len(); // 获取字符串的长度,
println!("hello length, {}", str_name);
}
综上,我们会发现,在同一作用域内,当我们使用let
申明同一个变量时,会存在变量遮蔽,不同作用域,会使用最近申明的遮蔽变量
注意在rust
中我们声明的变量是小驼峰命名。当我们使用的一个mut
申明的变量,再没有使用时候,会警告
rust
fn main() {
let mut y: u32 = 5;
y = 10;
println!("hello2, {}", y);
}
数据类型
在rust
中主要有以下两大类类型标量类型
、复合类型
。
其中标量类型
主要有四种:整型
、浮点型
、布尔型
、字符
。
复合类型
主要有元组类型
、数组类型
。
在以上我们使用let
定义了一个不可变的变量,在rust
中在编译期就知道其变量类型。
如果我想把字符串类型转换成数字类型,那如何做到呢?
rust
fn main() {
let guess_number = "12";
let guess_number: u32 = guess_number.parse().expect("not a number");
println!("guess number, {}", guess_number);
}
我们通过调用guess_number.parse()
方法将字符串转换成了数字类型。
在整型
类型里主要有两大类,有符号(i)
类型、无符号(u)
,在有符号(i)类型
主要有i8(-128~127),i16,i32,i64,i128
,在无符号(u)
上类型主要有u8(0-255),u16(0-2^16-1),u32(2^32-1),u64,u128
- 整型
rust
// 有符号类型
let x:i8 = 10;
println!('{}',x);
// 无符号类型
let y:u8 = 20;
println!('{}',y);
- 浮点型
rust
let float_number:f32 = 2.5;
println!("float_number,{}", float_number);
- 布尔类型
rust
let is_true:bool = true;
println!('float_number,{}', is_true);
- 字符串型
rust
let a_num:&str = 'hello'
复合类型
再rust
中复合类型主要包含以下两种,一是元组
(多种类型组合在一起形成一种复合类型,也就是元组),二是数组
(同一种数据类型组合在一起,且有长度)
- 元组类型
定义的类型一一对应,主要有(i32,f64,u8)
三种类型
rust
fn main() {
let tuple_number: (i32,f64, u8) = (800,1.2,5);
println!('tuple_number-0', tuple_number.0);
println!('tuple_number-1', tuple_number.1);
println!('tuple_number-2', tuple_number.2);
}
对元组进行解构
rust
let (x,y,z) = tuple_number;
- 数组 可以有以下几种方式
rust
let name_array:[i32;5] = [1,2,3,4,5];
let name_array = ['a';5];
// 以上等价于
let name_array = ['a','a', 'a', 'a', 'a']
如何获取一个数组栈内存分配的大小
mem::size_of_val(&DATA_NUMBER)
获取栈内存的大小
rust
use std::mem;
fn main() {
// 申明数组的长度
const DATA_NUMBER: [i32; 5] = [1, 2, 3, 4, 5];
println!("data is length {}", DATA_NUMBER.len());
// data在内存中
println!("data is {:?}", mem::size_of_val(&DATA_NUMBER));
}
自定义类型
在rust
中可以自定义类型,主要自定义类型方式有两种,一是结构体
,二是枚举
方式
#[derive(Debug)]
rust
#[derive(Debug)]
struct Person {
name: String,
age: i8,
}
fn main() {
let name = String::from("Maic");
let age = 18;
let user_info = Person {name, age};
println!("user.name:{},user.age:{}", user_info.name, user_info.age);
// 解构
let Person{name, age} = user_info;
println!("{},{}",name, age);
}
- 解构元组
rust
// 带有两个字段的解构体
struct Point {
x:f32,
y:f32
}
struct Pair(i32,f32);
fn main() {
let x = 1.1;
let y = 1.2;
let point:Point = Point {x,y};
println!("x={},y={}",point.x,point.y);
let pair = Pair(1.1,3.4);
let Pair(x,y) = pair;
println!("x={},y={}", x,y);
}
枚举
我们使用enum
关键词定义了WebEvent
,在这个枚举中我们看到有 PageLoad
、PageUnload
、KeyPress(char)
、Paste(String)
、Click {x: i64, y:i64}
rust
// 主要用于隐藏对未使用代码的警告
#![allow(dead_code)]
enum WebEvent {
PageLoad,
PageUnload,
KeyPress(char), // 元组结构体
Paste(String),
Click { x: i64, y: i64 },
}
// 将枚举WebEvent当成inspect的形参
fn inspect(event: WebEvent) {
match event {
WebEvent::PageLoad => println!("page loaded"),
WebEvent::PageUnload => println!("page unload"),
WebEvent::KeyPress(c) => println!("keypress:{}", c),
WebEvent::Paste(s) => println!("Paste,{}", s),
WebEvent::Click { x, y } => {
println!("x={},y={}", x, y)
}
}
}
fn main() {
let pressed = WebEvent::KeyPress('x');
let pasted = WebEvent::Paste("my name".to_owned());
let click = WebEvent::Click { x: 20, y: 80 };
let load = WebEvent::PageLoad;
let unload = WebEvent::PageUnload;
let x = OtherVeryVerBoseEnum::Add;
inspect(pressed);
inspect(pasted);
inspect(click);
inspect(load);
inspect(unload);
println!("Hello, world!");
}
use
当我们使用enum
申明了一个枚举时,在main
方法中可以使用use
关键词引入Work
rust
#![allow(dead_code)]
enum Work {
Civilian,
Soldier
}
fn main () {
use Work::*;
// 与下面等价
/*
use Work::{Civilian, Soldier}
*/
let work_civilian = Civilian;
match work_civilian {
Civilian => println!("the civilian"),
Soldier => println!("soldiers fight")
}
}
if
if
条件语句,当我们使用if
条件时,可以将判断后的语句进行赋值操作
js
fn main() {
let number = 10;
// 注意if后面并没有括号
let new_number = if number < 0 {
println!("to small");
-10 // 注意这里没有分号,会赋值给表达式new_number
} else {
println!("to big");
100 // 这里也会赋值表达式new_number
};
println!("{}", new_number);
}
for循环
循环一个范围数据,比如[a,b]之间
rust
fn main() {
// for循环a..=b 表示[a,b]范围
for item in 1..=10 {
println!("item1: {}", item);
}
// a..b 表示[a,b)范围
for item in (1..11).rev() {
println!("item,{}", item);
}
// vec!申明一个可迭代器的变量
let names = vec!["a", "b", "c"];
for item in names.iter() {
match *item {
"a" => println!("There is a rustacean among us!"),
_ => println!("Hello {}", item),
}
}
}
可以修改循环中的数组,使用names3.iter_mut()
rust
let mut names3 = vec!["Bob", "Frank", "Ferris"];
for name in names3.iter_mut() {
*name = match name {
&mut "Ferris" => "hello",
_ => "a",
}
}
println!("names3:{:?}", names3);
// names3:["a", "a", "hello"]
match
匹配对应条件,并返回,当我们使用match
时,如果条件不满足会默认走_ => println!("other case!")
,如果满足其中任意条件,则会走其中任意一分支,如果match
返回一个值,则会走match
匹配的分支
rust
fn main() {
let number = 2;
println!("number is {}", number);
match number {
1 => println!("first one"),
2 | 3 => println!("{}", number),
5..=6 => println!("good luck"),
_ => println!("other case!"),
};
let boolean = false;
let binary = match boolean {
false => "false",
true => "true",
};
println!("{} -> {}", boolean, binary);
}
enum
rust
// 消除警告,使用枚举类型取值
#[allow(dead_code)]
// 加了这个不会报错,第15【Color::Red => println!("color is red,{:?}", color)】行不会报错
#[derive(Debug)]
enum Color {
Red,
Blue,
}
fn main() {
// &表示取引用
let reference = &4;
let color = Color::Red;
match color {
Color::Red => println!("color is red,{:?}", color),
Color::Blue => println!("color is blue"),
}
match reference {
// &val指向的是reference
&val => println!("value is {}", val),
}
// 等价于上面,使用*引用,这样val不用加&
match *reference {
val => println!("value2 is {}", val),
}
// 没有引用
let no_reference = 3;
match no_reference {
val => println!("value3 is {}", val),
}
match no_reference {
ref val => println!("value4 is {}", val),
}
let mut value = 10;
match value {
ref mut val => {
// 先用*解引用,然后+10
*val += 10;
println!("value5 is {}", val);
}
}
}
总结
-
我们学习到了
rust
中的可变变量与不可变量,通常来讲使用let
申明的变量是不可变的,而且在rust
中可以重复定义同一变量名,但此时会发生遮蔽变量。 -
我们学习了
rust
中最基本的数据类型,主要分两大类,标量类型
与复合类型
,其中标量类型主要包含,整型
、浮点型
、布尔型
、字符串型
,复合类型主要包括,元组类型
与数组类型
-
了解到
use
,if
,match
,for
,enum
的使用 -
本文示例代码code example