0%

rust基本语法

数据类型

byte外的所有数字字面值允许使用类型后缀,如57u8

在debug模式中,整形溢出会导致程序panic。而release模式中,则会进行回环(Wrapping

如果确实需要整形溢出,可以使用Wrapping类型

1
2
3
let max = Wrapping(i32::MAX);//用标准库的Wrapping<T>结构体包装基本数据类型的数字
//或者直接调用.wrapping_***(...)成员方法来完成算术运算
assert_eq!(i32::MAX.wrapping_add(2), i32::MIN+1);
1
2
3
4
5
6
7
8
let a = 123_4; //整数中可以出现下划线
let tup:(i32,f64,u8) = (500,6.4,1);//元组可以包含不同种类的数据
//tup.0等于500。。。
let (x,y,z) = tup;//解构元组

let a = [1,2,3,4,5];//数组包含同类型数据
let a: [i32;5] = [1,2,3,4,5];//[元素类型;元素数量]
//当索引超出了下标范围时会发生一个运行时错误

字符串

str是Rust核心语言类型,常常以引用的形式出现(&str)

凡是用双引号包括的字符串常量的类型都是&str

String 则有更完善的功能

1
2
let s1 = String::from("Hello");
let s2 = &s1[..];//从String类型转为&str类型

可以用+号运算符进行简单的拼接

1
2
3
let s1 = String::from("Hello,");
let s2 = String::from("world!");
let s3 = s1 + &s2;//s1 在此之后不可用

+运算符使用了add函数,这个函数签名看起来像这样

1
2
3
fn add(self, s: &str) -> String {
...
}

之所以能在add调用中使用&s2是因为&String可以被强制转换成&str

对于更为复杂的字符串链接,可以使用format!宏:

1
2
3
4
5
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{}-{}-{}",s1,s2,s3);
//format!与println!的工作原理相同,不同之处是其返回一个带有结果内容的String

使用parse或turbo fish语法实现从字符串到其他类型的转换:

1
2
3
4
5
6
7
8
9

//只要目标类型实现了FromStr特性
fn main(){
let parsed: i32 = "5".parse().unwrap();
let turbo_parsed = "10".parse::<i32>().unwrap();

let sum = parsed + turbo_parsed;
println!{"Sum: {:?}",sum};
}

内部表现

String是一个Vec<u8>的封装,而Unicode是使用两个字节表示一个字,因此String不支持下标访问

结构体

元组常用于非定义的多值传递,而结构体用于定义对象规范,结构体的每个成员叫做“字段”

1
2
3
4
5
6
7
8
//一个结构体定义
struct Site{
domain: String,
name: String,
nation: String,
found: u32
//每个字段定义后用","分隔
}
1
2
3
4
5
6
7
8
//结构体实例
let mysite = Site{
//key: value 语法
domain: String::from("ajsoabk.xyz"),
name: String::from("Ajsoabk"),
nation: String::from("China"),
found: 2020
};//记得分号
1
2
3
4
5
6
7
8
9
10
11
12
13
let domain = String("ajsoabk.xyz");
let name = String("Ajsoabk");
let mysite = Site{
domain,//有同名变量时可以简化
name,//等同于name: name
nation: String:from("China"),
found: 2020
};
//结构体更新语法
let newsite = Site{
domain: String("www.ajsoabk.xyz"),
..mysite
};//这种语法需要至少重新设定一个字段的值

元组结构体

形式是元组的结构体

1
2
3
4
5
6
7
struct Color(u8,u8,u8);
struct Point(f64,f64);

let black = Color(0,0,0);
let origin = Point(0.0,0.0);
println!("black = ({},{},{})",black.0,black.1,black.2);
println!("origin = ({},{})",origin.0,origin.1);

输出结构体

1
2
3
4
5
6
7
8
9
10
#[derive(Debug)]//导入调试库
struct Rectangle{
width: u32,
height: u32,
}
fn main(){
let rect1= Rectangle{width:30,height:50};
println!("rect1 is {:?}",rect1);//使用{:?}占位符输出结构体
println!("rect1 is {:#?}",rect1)://使用{:#?}占位符分行输出结构体
}

结构体方法

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Rectangle{
width: u32,
height:32,
}
impl Rectangle{
fn area(&self) -> u32{//结构体方法的第一个参数必须是&self,不需要声明类型
self.width * self.height
}
}
fn main(){
let ret1 = Rectangle {width : 30,height: 50};
println!("rect1's area is {}",rect1.area());
}

结构体关联函数

结构体关联函数与结构体方法的区别是结构体关联函数在impl块中却没有&self 参数

这种函数不依赖实例,但却需要声明是在哪个impl块中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#[derive(Debug)]
struct Rectangle{
width: u32,
height: u32,
}
impl Rectangle{
fn create(width: u32,height: u32) -> Rectangle{
Rectanlge{width,height}
}
}
fn main(){
let rect = Rectangle::create(30,50);
println!("{:?}",rect);
}

impl块可以写多次,相当于将其拼接

1
2
3
4
5
6
7
8
//对于非枚举类,需要设置缺省情况
fn main(){
let t = "abc";
match t {
"abc" => println!("Yes"),
_ => {},
}
}

Option、Result与kind

Option类包含None及Some,用于可能有空值的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fn check_division(dividend: i32,divisor: i32) -> Option<i32>{
if divisor == 0{
None
}
else {
Some(dividend / divisor)
}
}
fn try_division(dividend: i32,divisor: i32){
match checked_division(dividend,divisor){
None => println!("{} / {} failed!",dividend,divisor);
Some(quotient) => {
println!("{} / {} = {}",dividend, divisor, quotient)
},
}
}
fn main(){
try_division(4,2);
try_division(1,0);
}

Result类则可以包含相关错误信息,还有专门的?操作符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fn f(i: i32) -> Result<i32, bool> {
if i >= 0 { Ok(i) }
else { Err(false) }
}
fn g(i: i32) -> Result<i32, bool> {
let t = f(i)?;//在Result出现异常的时候直接返回异常
Ok(t)
}
fn main(){
let r = g(10000);
if let Ok(v) = r{
println!("Ok: g(10000) = {}", v);
} else {
println!("Err");
}
}

Result类还有kind方法可以确定错误类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use std::io;
use std::io::Read;
use std::fs::File;
fn read_text_from_file(path: &str) -> Result<String, io::Error>{
let mut f = File::open(path)?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
fn main(){
let str_file = read_text_from_file("hello.txt");
match str_file{
Ok(s) => prinln!("{}",s);
Err(e) => {
match e.kind(){//根据错误类型进行相应处理
io::ErrorKind::NotFound => {
println!("No such file");
},
_ => {
println!("Cannot read the file");
}
}
}
}
}

vector

1
2
3
4
5
6
7
8
9
let v= vec![1,2,3,4,5];//用vec!宏进行初始化
let mut v:Vec<i32> = Vec::new();//新建vector变量
v.push(5)
println!("The third element is {}",v[0]);//访问下标为0的位置上的元素,若超出则会崩溃
//或者使用get方法,可以自己选择超出后的处理方法
match v.get(2) {
Some(i) => println!("The third element is {}",i),
None => println!("There is no third element."),,
}

控制流语句(if,while,for,loop,if let, match)

lf-else 作为表达式

1
2
3
let a = 3;
let b = 4;
println!("the max num in {} and {} is {}",a,b,if a > b {a} else {b});

使用loop查找

1
2
3
4
5
6
7
8
9
let s = ['X','i','o','n','g','F','a','n','g'];
let mut i = 0;
let location = loop {
if(s[i] == 'F'){
break i
}
i += 1;
};
println!("The location of \'F\' is {}",location);

match 和if let

1
2
3
4
5
6
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),//匹配的同时绑定i
}
}

匹配必须穷尽

1
2
3
4
5
let some_u8_value = Some(0u8);
match some_u8_value {
Some(1) => println!("one"),
_ => (),//所有剩余情况的匹配
}

可以使用if let 来进行匹配且无需穷尽

1
2
3
4
5
6
fn plus_one(x:Option<i32>) -> Option<i32> {
if let Some(i) = x {
return Some(i+1);
}
None
}

所有权机制

  • Rust中的每个值都有一个变量,称为其所有者
  • 一次只能有一个所有者
  • 当所有者不在程序运行范围内时,该值将被删除

移动

1
2
3
4
let x = 5;
let y = x;
//此时栈中有两个值5,因为int是基本数据类型,基本数据类型具有Copy特性
//Copy特性保证其对象在被赋值给其他对象后仍然可用

整型、布尔类型、浮点型、字符类型和仅包含以上类型的元组是基本数据类型

但如果发生交互的数据在堆中就是另外一种情况

1
2
3
let s1 = String::from("Hello");//"Hello"可以认为是类似于长度不确定的数据,需要在堆中存储
let s2 = s1;//由于两者都指向堆中变量,因此为了防止两次释放,s1被赋值给s2后,s1已经无效了
println!("{} World!",s1); //错误!s1已经失效

克隆

1
2
3
let s1 = String::from("Hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}",s1,s2);

涉及函数的所有权机制

如果讲变量当作参数传入函数,那么它和移动的效果是一样的

被当作函数返回值的变量所有权将会被移动出函数并返回到调用函数的地方,而不会直接被无效释放

引用与租借

1
2
3
let s1 = String::from("Hello");
let s2 = &s1;
println!("s1 is {}, s2 is {}",s1,s2);

&可以取变量的引用,引用并没有在栈中复制变量的值

引用不会获得值的所有权。引用只能租借值的所有权,引用本身也是一个类型并具有一个值,这个值记录的是别的值所在的位置,但引用不具有所指值的所有权

1
2
3
4
5
//一段不正确的代码
let s1 = String::from("Hello");
let s2 = &s1;
let s3 = s1;
println!("{}",s2);//错误,所有权已经转给s3,s2需要重新从s3租借所有权
1
2
3
4
5
let s1 = String::from("Hello");
let mut s2 = &s1;
let s3 = s1;
s2 = &s3;
println!("{}",s2);//正确

如果尝试利用租借来的权力来修改数据会被阻止

可变引用

可以使用可变引用来修改数据

1
2
3
4
let mut s1 = String::from("Hello");
let s2 = &mut s1;
s2.push_str(" World!");
println!("{}",s2);

可变引用不允许多重引用(不管是可变还是非可变)

1
2
3
4
5
//一段不正确的代码
let mut s = String::from("Hello");
let r1 = &mut s;
let r2 = &mut s;//错误,可变引用不允许多重引用
println!("{},{}",r1,r2);

主要是为了防止在并发状态下发生数据访问碰撞

错误的悬垂引用

1
2
3
4
5
6
7
fn main(){
let reference_to_nothing = dangle();
}
fn dangle() -> &String{
let s =String::from("Hello");
&s//此时s已经被释放,但s的引用却被返回,这个引用所指向的值已经不存在
}

编译器会发现这种错误

Rust 组织管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mod nation{
pub mod government{
pub fn govern(){}//pub公开路径
}
mod congress{
pub fn legislate(){}
}
mod court{
fn judicial(){
super::congress::legislate();//super进入上一级
}
}
pub use congress::legislate;//pub use使外部代码也能直接使用新路径
}
use nation::{government::govern,legislate};//嵌套路径
use std::io::{self, Write};//可用self指定自身
use std::collections::*;//用*指定所有公有项

Rust泛型与特性

1
2
3
4
5
6
7
8
9
10
11
12
struct Point<T,U> {
x:T,
y:U,
}
impl<T,U> Point<T,U> {
fn mixup<V,W>(self,other: Point<V,W>) -> Point<T,W>{
Point{
x: self.x,
y: other.y,
}
}
}

一个类可以实现多个特性,每个impl块只能怪为一个类实现一个特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
trait Comparable {
fn compare(&self, object: &Self) -> i8;//描述Comparable特性
}
fn max<T: Comparable> (array: &[T]) -> &T {
let mut max_index = 0;
let mut i = 1;
while i< array.len() {
if array[i].compare(&array[max_index]) > 0{
max_index = i;
}
i += 1;
}
&array[max_index]
}
impl Comparable for f64 {
fn compare(&self, object: &f64) -> i8{
if &self > &object {1}
else if &self == &object {0}
else {-1}
}
}
fn main(){
let arr = [1.0,3.0,5.0,4.0,2.0];
println!("maximum of arra is {}",max(&arr));
}

默认特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
trait Descriptive{
fn describe(&self) -> String {
String::from("[Object]")
}
}
struct Person {
name: String,
age: u8
}
impl Descriptive for Person{
fn describe(&self) -> String {
format!("{} {}",self.name,self.age)
}
}
fn main(){
let cali = Person{
name: String::from("Cali"),
age: 24
};
println!("{}",cali.describe());
}