"首次提交"
This commit is contained in:
parent
6e0f708d0a
commit
84349a2469
4
.gitignore
vendored
Normal file → Executable file
4
.gitignore
vendored
Normal file → Executable file
|
@ -13,4 +13,8 @@ Cargo.lock
|
|||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
*.exe
|
||||
|
||||
*/.git
|
||||
|
||||
*/.vscode
|
1
enum_learn/.gitignore
vendored
Executable file
1
enum_learn/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
enum_learn/Cargo.toml
Executable file
8
enum_learn/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "enum_learn"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
118
enum_learn/src/main.rs
Executable file
118
enum_learn/src/main.rs
Executable file
|
@ -0,0 +1,118 @@
|
|||
use std::{net::IpAddr, string};
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
enum_learn();
|
||||
enum_option();
|
||||
}
|
||||
|
||||
fn enum_learn(){
|
||||
|
||||
//定义枚举使用关键字enum,不附带数据
|
||||
enum IpAddr_test{
|
||||
V4,
|
||||
V6,
|
||||
}
|
||||
struct ipv4{
|
||||
addr0:u8,
|
||||
addr1:u8,
|
||||
addr2:u8,
|
||||
addr3:u8,
|
||||
}
|
||||
//定义枚举并附加一些数据
|
||||
enum IpAddr{
|
||||
//元组
|
||||
V4(u8,u8,u8,u8),
|
||||
V6(String),
|
||||
//匿名结构体
|
||||
V4_1{addr0:u8, addr1:u8, addr2:u8, addr3:u8},
|
||||
//具名结构体, 使用元组和匿名结构体的方式组织
|
||||
V4_2(ipv4),
|
||||
V4_3{addr:ipv4},
|
||||
}
|
||||
//可通过impl 定义枚举的方法&关联函数。和struct类型的impl的使用类似
|
||||
impl IpAddr{
|
||||
fn print_addr(&self){
|
||||
//使用match方法匹配枚举值,并提取出其相关的数据使用
|
||||
match self {
|
||||
//使用元组的方式那么这里对应的数据也是需要用 () 进行解构的
|
||||
IpAddr::V4(val0,val1,val2,val3) => {
|
||||
println!("addr is {}.{}.{}.{}", val0, val1,val2,val3);
|
||||
},
|
||||
IpAddr::V6(addr_str) => {
|
||||
println!("addr is {}", addr_str);
|
||||
},
|
||||
//使用结构体的方式那么这里对应的数据也是需要用 {} 进行解构的
|
||||
IpAddr::V4_1 { addr0, addr1, addr2, addr3 } => {
|
||||
println!("addr is {}.{}.{}.{}", addr0, addr1,addr2,addr3);
|
||||
},
|
||||
IpAddr::V4_2(addr) => {
|
||||
println!("addr is {}.{}.{}.{}", addr.addr0, addr.addr1, addr.addr2, addr.addr3);
|
||||
},
|
||||
IpAddr::V4_3{addr} => {
|
||||
println!("addr is {}.{}.{}.{}", addr.addr0, addr.addr1, addr.addr2, addr.addr3);
|
||||
},
|
||||
//匹配其他情况
|
||||
_ => {
|
||||
println!("not support");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//函数体内可以再定义函数。其作用域只在本函数内
|
||||
fn sub(){
|
||||
//使用元组的方式初始化
|
||||
let home = IpAddr::V4(127,0,0,1);
|
||||
let lookup = IpAddr::V6(String::from("::"));
|
||||
//使用结构体的方式初始化
|
||||
let home1 = IpAddr::V4_1{addr0:127,addr1:0,addr2:0,addr3:1};
|
||||
let addr = ipv4{addr0:127,addr1:0,addr2:0,addr3:1};
|
||||
//具名结构体,使用元组
|
||||
let home2 = IpAddr::V4_2(addr);
|
||||
//具名结构体,使用结构体的方式初始化
|
||||
let home3 = IpAddr::V4_3{addr:ipv4{addr0:127,addr1:0,addr2:0,addr3:1}};
|
||||
|
||||
home.print_addr();
|
||||
lookup.print_addr();
|
||||
home1.print_addr();
|
||||
home2.print_addr();
|
||||
home3.print_addr();
|
||||
}
|
||||
//调用函数, 如果局部的函数和全局的有同名的。优先局部的
|
||||
sub();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 在rust中没有NULL的使用,其使用一个特殊的枚举Option<T> 来表达可能有值或者没有值的意思
|
||||
想要使用Option的变量必须对其进行转化,转为T方可使用,从而保证了其在可以使用时一定是有值的。
|
||||
*/
|
||||
fn enum_option(){
|
||||
let mut num1 = Some(2); // 编译器可自动推导出类型 T
|
||||
let num2 = 10;
|
||||
let num3: Option<i64> = Some(5);// 也可以显式进行指定
|
||||
let num4: Option<i32> = None; // 如果未None时编译器无法自动推断类型,所以需要显式进行指定 T 是什么类型
|
||||
let str1 = Some(String::from("a Option string"));
|
||||
|
||||
// case1: 不能直接使用 + 来操作一个Option<T>和T,因为是两个不同的类型,不能直接相加
|
||||
//let ret1:i32 = num1 + num2;
|
||||
fn add(num1:Option<i32>, num2:i32) -> i32{
|
||||
match num1 {
|
||||
Some(num) => num + num2,// 使用match仅仅在其有值的情况下进行使用
|
||||
None => {
|
||||
println!("num1 is None!");
|
||||
num2
|
||||
},
|
||||
}
|
||||
}
|
||||
let ret1 = add(num1, num2);
|
||||
println!("{:?}(num1) + {}(num2) => ret1: {}", num1, num2, ret1);
|
||||
|
||||
// case2: Option<T> 没有 + 操作符,也不能直接相加
|
||||
//let ret2:i32 = num1 + num3;
|
||||
|
||||
num1 = num4; // 可以将 Option<T> (T相同) 的变量之间进行所有权的转移
|
||||
//num1 = num3; // num3 是 Option<i64>, num1是 Option<i32> 不能绑定
|
||||
|
||||
println!("{:?}(num1) + {}(num2) => ret2: {}", num1, num2, ret2);
|
||||
}
|
1
function/.gitignore
vendored
Executable file
1
function/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
function/Cargo.toml
Executable file
8
function/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "function"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
65
function/src/main.rs
Executable file
65
function/src/main.rs
Executable file
|
@ -0,0 +1,65 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
another_function();
|
||||
display(4);
|
||||
display(5);
|
||||
let ret = inc(5);
|
||||
println!("5 + 1 = {}", ret);
|
||||
let ret = inc1(5);
|
||||
println!("5 + 1 = {}", ret);
|
||||
let ret = inc2(5);
|
||||
println!("5 + 1 = {}", ret);
|
||||
ThisIsAfunction();
|
||||
}
|
||||
|
||||
|
||||
//定义一个函数,。使用关键字fn
|
||||
//函数的定义可以在调用后,无需先申明后使用
|
||||
fn another_function (){
|
||||
println!("another_function");
|
||||
}
|
||||
|
||||
//函数的param必须标注类型但是不能类型前置
|
||||
//fn display(i32 x){
|
||||
fn display(x:i32){
|
||||
println!("x is {}", x);
|
||||
}
|
||||
|
||||
/*
|
||||
注释基本和C语言一致,多行支持使用 /* */的方式
|
||||
函数指定返回值必须使用后置,使用 -> 的方式进行返回类型的指定,和pythno的类型标注类似,但是python是可选的。
|
||||
*/
|
||||
fn inc(x:i32) -> i32{
|
||||
let ret = x + 1;
|
||||
return ret
|
||||
}
|
||||
|
||||
fn inc1(x:i32) -> i32{
|
||||
//函数的返回值默认为最后一个表达式的值作为返回值,
|
||||
//所以这里不能加上分号,加上分号就变成了语句了
|
||||
x + 1
|
||||
}
|
||||
|
||||
//rust的函数明明必须使用snake_style 下面这种风格会有警告
|
||||
fn ThisIsAfunction(){
|
||||
}
|
||||
|
||||
fn inc2(x:i32) -> i32{
|
||||
/*
|
||||
两个大括号括起来的是一个代码块,
|
||||
其最后一个表达式的值会作为代码块的最终结果值,
|
||||
和C语言的宏块({...}) 是类似,不过C里面最后的返回值的代码也要加分号
|
||||
*/
|
||||
//所以这里使用y作为代码块的返回值,并且后面不能加分号
|
||||
let ret = {
|
||||
let y = x + 1;
|
||||
y
|
||||
}; //这里必须要加分号,因为 let ret ={...}; 加了分号才属于一整个完整的语句,不然语法错误了,{...} 这部分属于表达式
|
||||
/*
|
||||
以下几种返回方式都是允许的 但是表达式的风格必须只能是最后一句,
|
||||
不然中间的代码是必须加分号的,因为不加的话就构不成完整的语句了
|
||||
*/
|
||||
//{ret} //方式1: ret作为代码块的值,代码块的值又作为函数的返回值,但是没必要
|
||||
//ret //方式2:单独表达式作为函数返回值
|
||||
return ret; //方式3:当函数提前返回时必须使用return 关键字,当然其放到最后显式的调用也是ok的
|
||||
}
|
1
generics/.gitignore
vendored
Executable file
1
generics/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
9
generics/Cargo.toml
Executable file
9
generics/Cargo.toml
Executable file
|
@ -0,0 +1,9 @@
|
|||
workspace = { members = ["trait_learn"] }
|
||||
[package]
|
||||
name = "generics"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
68
generics/src/main.rs
Executable file
68
generics/src/main.rs
Executable file
|
@ -0,0 +1,68 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
generics();
|
||||
generics1();
|
||||
}
|
||||
|
||||
|
||||
fn generics() {
|
||||
// 定义一个支持多种类型的结构体,元素的类型取决于实际代码
|
||||
struct Point <T, U> {
|
||||
x:T,
|
||||
y:U,
|
||||
}
|
||||
let p1 = Point {
|
||||
x : 10, //i32
|
||||
y : 'c', //char
|
||||
};
|
||||
let p1 = Point {
|
||||
x : 10, //i32
|
||||
y : 10.3, // f64
|
||||
};
|
||||
let p1 = Point {
|
||||
x : '1', // char
|
||||
y : 10.3, // f64
|
||||
};
|
||||
}
|
||||
|
||||
fn generics1() {
|
||||
#[derive(Debug)]
|
||||
struct Point <T, U> {
|
||||
x:T,
|
||||
y:U,
|
||||
}
|
||||
|
||||
//对于point的实现也使用泛型,
|
||||
/* 这里impl后加 <T, U> 是因为后续的Point里面指定的类型T,U就是由此来的
|
||||
而这里不加V和W是因为V和W是函数mixup中才使用的并不是和Point有关的泛型,所以不需要再这里加T和U,
|
||||
泛型的顺序是没关系。仅仅是为了指明有哪些类型。使用时对应即可
|
||||
*/
|
||||
impl<T, U> Point<T, U> {
|
||||
//impl<U, T> Point<T, U> { // 这是正确的
|
||||
//impl<T, U, V, W> Point<T, U> { //这是错误的
|
||||
fn new(x:T, y:U) -> Point<T, U>{
|
||||
Point{
|
||||
x,
|
||||
y,
|
||||
}
|
||||
}
|
||||
|
||||
//这里使用了泛型V和W是为了有别于上面的T和U,因为self的类型已经是T和U了,
|
||||
//如果这里还是T和U那么其实就是限制了other的类型,必须是和self一样的类型
|
||||
//这里使用V和W就是表示可以是任意的类型。
|
||||
//返回值是T和W就是说明他用的是self的x和other的Y
|
||||
//fn mixup <I, V, W>(self, other: Point<V,W>, z:I) -> Point<T,W>{ // 有其他参数就继续加泛型名即可
|
||||
fn mixup <V, W>(self, other: Point<V,W>) -> Point<T,W>{
|
||||
Point{
|
||||
x:self.x,
|
||||
y:other.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let point = Point::new(32, 'a');
|
||||
let point1 = Point::new(23.0, 100);
|
||||
|
||||
let point2 = point1.mixup(point);
|
||||
println!("{:?}", point2);
|
||||
}
|
1
guessing_game/.gitignore
vendored
Executable file
1
guessing_game/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
9
guessing_game/Cargo.toml
Executable file
9
guessing_game/Cargo.toml
Executable file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "guessing_game"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
rand='0.8.5'
|
29
guessing_game/src/main.rs
Executable file
29
guessing_game/src/main.rs
Executable file
|
@ -0,0 +1,29 @@
|
|||
//prelude 预导入: Rust 会导入一些默认的模块供给使用,但是其他的就需要手动使用use导入
|
||||
use std::{cmp::Ordering, io};
|
||||
use rand::{Rng, thread_rng};
|
||||
|
||||
fn main() {
|
||||
println!("猜数游戏");
|
||||
|
||||
let mut rng = thread_rng();
|
||||
let secret_num:u32= rng.gen_range(1..100);
|
||||
// println!("神秘数字是 {}", secret_num);
|
||||
loop {
|
||||
println!("请猜测一个数字");
|
||||
let mut guess = String::new();
|
||||
let str_len = io::stdin().read_line(&mut guess).expect("读取行错误错误");
|
||||
// println!("你输入的内容长度是 {}, 内容为:{}",str_len, guess);
|
||||
let guess:u32 = match guess.trim().parse() {
|
||||
Ok(num) => num,
|
||||
Err(_) => {
|
||||
println!("字符串非数字无法解析");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
match guess.cmp(&secret_num){
|
||||
Ordering::Less => println!("你猜的数字太小"),
|
||||
Ordering::Greater => println!("你猜的数字太大"),
|
||||
Ordering::Equal => {println!("你猜对了");break;},
|
||||
}
|
||||
}
|
||||
}
|
1
hash_map/.gitignore
vendored
Executable file
1
hash_map/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
hash_map/Cargo.toml
Executable file
8
hash_map/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "hash_map"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
46
hash_map/src/main.rs
Executable file
46
hash_map/src/main.rs
Executable file
|
@ -0,0 +1,46 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
hash_map_learn();
|
||||
}
|
||||
|
||||
|
||||
fn hash_map_learn() {
|
||||
|
||||
/* 创建一个hashmap,使用显式指定key和value的类型的方式
|
||||
创建时也可以不指定类型,使用insert方法进行元素的插入时即可自动推导出类型
|
||||
不过一旦类型确定所有key的类型必须一致。所有value的类型也必须一致。
|
||||
*/
|
||||
let mut map1:HashMap<String, i32> = HashMap::new();
|
||||
|
||||
//插入一个元素
|
||||
map1.insert(String::from("aa"), 1);
|
||||
//插入同名元素,会覆盖原有的元素
|
||||
map1.insert(String::from("aa"), 2);
|
||||
println!("{:?}", map1);
|
||||
//尝试访问并在没有对应数据的情况下插入新的值,这样可以做到不覆盖已有元素
|
||||
map1.entry(String::from("aa")).or_insert(3);// 如果元素aa不存在则插入aa:3
|
||||
map1.entry(String::from("bb")).or_insert(4);// 如果元素aa不存在则插入bb:4
|
||||
println!("{:?}", map1);
|
||||
|
||||
let e = map1.entry(String::from("cc"));
|
||||
println!("{:?}", e);
|
||||
let e = map1.entry(String::from("aa"));
|
||||
println!("{:?}", e);
|
||||
|
||||
//利用hashmap统计单词的个数。
|
||||
let str1 = String::from("hello world wonderful world 你好 世界 美丽 世界");
|
||||
let mut map1 = HashMap::new();
|
||||
//将单词使用空格切割开 并遍历
|
||||
for word in str1.split_whitespace() {
|
||||
//这里or_insert的返回值是hashmap对应word的键值对的值的可变引用,
|
||||
//这里就是如果没找到word那么就插入一个word和cnt的键值对。值就是0。并将值的可变引用返回
|
||||
//这里如果找到对应的word那么就不会覆盖原来的值,仅仅是将值的可变引用返回
|
||||
//所以无论如何我们都能获得一个该word对应出现次数的可变引用
|
||||
let count = map1.entry(word).or_insert(0);
|
||||
//将值所对应的可变引用进行+1,表示数字个数多了一个
|
||||
*count = *count + 1;
|
||||
}
|
||||
println!("{:?}", map1);
|
||||
}
|
1
hello_cargo/.gitignore
vendored
Executable file
1
hello_cargo/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
hello_cargo/Cargo.toml
Executable file
8
hello_cargo/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "hello_cargo"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
3
hello_cargo/src/main.rs
Executable file
3
hello_cargo/src/main.rs
Executable file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
1
if_else/.gitignore
vendored
Executable file
1
if_else/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
if_else/Cargo.toml
Executable file
8
if_else/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "if_else"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
68
if_else/src/main.rs
Executable file
68
if_else/src/main.rs
Executable file
|
@ -0,0 +1,68 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
test_function()
|
||||
}
|
||||
|
||||
|
||||
fn test_function(){
|
||||
let num = 34;
|
||||
|
||||
//if num {//判断条件必须是bool类型的,此处num类型是i32,无法进行判断
|
||||
if num != 0 { // 使用这样的方法来产生一个bool类型的结果提供给if进行判断
|
||||
/* 当有多个条件时为了防止混乱可以在多个条件上加括号,条件组合的最终结果没必要加 和python类似 */
|
||||
//if (num != 0) && (num > 0) { //✅
|
||||
//if ((num != 0) && (num > 0)) { //❎
|
||||
println!("condition is true");
|
||||
}else{
|
||||
println!("condition is false");
|
||||
}
|
||||
|
||||
/* 使用if来构建三元表达式
|
||||
在C中三元运算符我们使用 cond ? true : false;
|
||||
在rust中if是表达式而不是语句,表达式都会产生一个结果,可以作为右值使用
|
||||
*/
|
||||
let cond = false;
|
||||
let ret = if cond {true} else {false}; //使用if构建的三元表达式
|
||||
|
||||
/* if 表达式可以多级使用 用法和C类似
|
||||
但是在rust中使用超过一个if else就应该考虑使用match (和c中的switch类似)
|
||||
*/
|
||||
let num = 2;
|
||||
if (num >= 4) && (num % 4 == 0) {
|
||||
println!("{} can div by 4", num);
|
||||
} else if num >= 3 && (num % 3 == 0) {
|
||||
println!("{} can div by 3", num);
|
||||
}
|
||||
else if (num > 2 || num == 2) && num % 2 == 0 {
|
||||
println!("{} can div by 2", num);
|
||||
}else{
|
||||
println!("{} cant div by 4 or 3 or 2", num);
|
||||
}
|
||||
|
||||
/* 在rust中if else的值必须保持一致!! */
|
||||
//case1:if 和else的代码块值类型不一致。if的是i32 else的是f64是不可以的,
|
||||
// 因为rust是静态强类型。在编译阶段必须直到ret的类型,但是如果if else类型不一致会导致无法推断!
|
||||
//let ret = if 2 == 2 {2} else {2.0};
|
||||
|
||||
//case2: 如果没有左值,也就是没有接收if表达式的变量,那么代码块里面就不能有值产生或者说值只能是()
|
||||
if 2 == 2 { //没有人要if表达式的结果
|
||||
//2 //❎ 不能产生值
|
||||
//() //✅ //代码块值只能是 ()
|
||||
}else{
|
||||
//2.0 //❎
|
||||
// //✅ //nothing,编译器自动认为是 ()
|
||||
}
|
||||
|
||||
//如果显式的规定的左值的结果那么if表达式里面的代码块值只能是左值类型
|
||||
//let ret: bool = if 2 == 2 {"true"} else{"false"}; //变量是bool,但是表达式里面是字符串 ❎
|
||||
//如果没有显式指定,编译器会自动推导
|
||||
let ret = if 2 == 2 {"true"} else{"false"}; //变量没有类型,编译自动推到出来 ✅
|
||||
|
||||
// test () value
|
||||
let ret = if 2 == 2 {} else{};
|
||||
if ret == () { //判断是是否为()
|
||||
println!("ret if ()")
|
||||
}else{
|
||||
println!("ret is not ()")
|
||||
}
|
||||
}
|
1
iterator_learn/.gitignore
vendored
Executable file
1
iterator_learn/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
iterator_learn/Cargo.toml
Executable file
8
iterator_learn/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "iterator_learn"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
176
iterator_learn/src/main.rs
Executable file
176
iterator_learn/src/main.rs
Executable file
|
@ -0,0 +1,176 @@
|
|||
use std::{env::VarsOs, path::Iter};
|
||||
|
||||
fn main() {
|
||||
iterator_learn();
|
||||
}
|
||||
|
||||
fn iterator_learn() {
|
||||
let v1 = vec![1, 2, 3];
|
||||
let iter = v1.iter();
|
||||
//使用循环遍历迭代器
|
||||
for it in iter {
|
||||
println!("{}", it);
|
||||
}
|
||||
//使用next方法遍历迭代器,
|
||||
let mut iter = v1.iter();
|
||||
loop {
|
||||
let item = match iter.next() {
|
||||
Some(it) => it,
|
||||
None => break,
|
||||
};
|
||||
println!("{}", item)
|
||||
}
|
||||
|
||||
let ret = v1.iter().skip(1);
|
||||
for it in ret {
|
||||
println!("{}", it);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iterator_demonstration() {
|
||||
let v1 = vec![1, 2, 3];
|
||||
|
||||
// next 方法 会获得所有权。必须将其变为mut
|
||||
let mut iter = v1.iter();
|
||||
|
||||
assert_eq!(iter.next(), Some(1).as_ref());
|
||||
assert_eq!(iter.next(), Some(2).as_ref());
|
||||
assert_eq!(iter.next(), Some(3).as_ref());
|
||||
assert_eq!(iter.next(), None);
|
||||
}
|
||||
#[test]
|
||||
fn iterator_create1() {
|
||||
let v1 = vec![1, 2, 3];
|
||||
|
||||
let mut iter = v1.iter().map(|x| x + 1);
|
||||
assert_eq!(iter.next(), Some(2));
|
||||
assert_eq!(iter.next(), Some(3));
|
||||
assert_eq!(iter.next(), Some(4));
|
||||
assert_eq!(iter.next(), None);
|
||||
|
||||
// collect 方法返回值需要手动指定。以便于rust编译推断其所需构造的类型
|
||||
let ret: Vec<i32> = v1.iter().map(|x| x + 1).collect();
|
||||
assert_eq!(vec![2, 3, 4], ret);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn closure_test() {
|
||||
let mut offset = 10;
|
||||
//闭包可以手动指定类型也可以自动推断,但是一旦确定一种调用方法后将不能再改变参数类型
|
||||
let func = |x: i32, y, z| -> i32 { x + y + z + offset };
|
||||
|
||||
fn sub_fun<F>(func: F, x: i32, y: i32, z: i32) -> i32
|
||||
where
|
||||
F: Fn(i32, i32, i32) -> i32,
|
||||
{
|
||||
func(x, y, z)
|
||||
}
|
||||
//offset = 20; // 不可以修改offset的值,因为其已经被闭包作为一个一个不可变引用使用了,
|
||||
assert_eq!(sub_fun(func, 1, 2, 3), 6 + offset);
|
||||
|
||||
//闭包的生命周期已经结束,所以offset可以被修改了
|
||||
offset = 20;
|
||||
assert_eq!(offset, 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_by_size() {
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct Shoe {
|
||||
size: u32,
|
||||
style: String,
|
||||
}
|
||||
fn shoes_in_my_size(shoes: Vec<Shoe>, size: u32) -> Vec<Shoe> {
|
||||
//into_iter 方法会获得shoes的所有权,所以不能再使用shoes
|
||||
shoes.into_iter().filter(|x| size == x.size).collect()
|
||||
}
|
||||
let shoes = vec![
|
||||
Shoe {
|
||||
size: 10,
|
||||
style: String::from("sneaker"),
|
||||
},
|
||||
Shoe {
|
||||
size: 13,
|
||||
style: String::from("sandal"),
|
||||
},
|
||||
Shoe {
|
||||
size: 10,
|
||||
style: String::from("boot"),
|
||||
},
|
||||
];
|
||||
|
||||
let ret = shoes_in_my_size(shoes, 10);
|
||||
//不可以再使用shoes,所有权已经到了 shoes_in_my_size 里面
|
||||
// shoes.push(Shoe {
|
||||
// size: 10,
|
||||
// style: String::from("sneaker"),
|
||||
// });
|
||||
assert_eq!(
|
||||
vec![
|
||||
Shoe {
|
||||
size: 10,
|
||||
style: String::from("sneaker"),
|
||||
},
|
||||
Shoe {
|
||||
size: 10,
|
||||
style: String::from("boot"),
|
||||
},
|
||||
],
|
||||
ret
|
||||
);
|
||||
}
|
||||
|
||||
struct Counter {
|
||||
cnt: u32,
|
||||
}
|
||||
|
||||
impl Counter {
|
||||
fn new() -> Counter {
|
||||
Counter { cnt: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Counter {
|
||||
type Item = u32;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.cnt < 5 {
|
||||
self.cnt += 1;
|
||||
Some(self.cnt)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_iterator() {
|
||||
let mut count = Counter::new();
|
||||
assert_eq!(count.next(), Some(1));
|
||||
assert_eq!(count.next(), Some(2));
|
||||
assert_eq!(count.next(), Some(3));
|
||||
assert_eq!(count.next(), Some(4));
|
||||
assert_eq!(count.next(), Some(5));
|
||||
assert_eq!(count.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_iterator1() {
|
||||
let count: Vec<(u32, u32)> = Counter::new()
|
||||
.zip(Counter::new().filter(|x| x % 2 == 0))
|
||||
.collect();
|
||||
assert_eq!(count, vec![(1, 2), (2, 4)]);
|
||||
let count: Vec<u32> = Counter::new()
|
||||
.zip(Counter::new().filter(|x| x % 2 == 0))
|
||||
.map(|(a, b)| a + b)
|
||||
.collect();
|
||||
|
||||
assert_eq!(count, vec![3, 6]);
|
||||
|
||||
let count: u32 = Counter::new()
|
||||
.zip(Counter::new().filter(|x| x % 2 == 0))
|
||||
.map(|(a, b)| a + b)
|
||||
.sum();
|
||||
assert_eq!(count, 9 );
|
||||
}
|
1
learn_test_8/.gitignore
vendored
Executable file
1
learn_test_8/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
learn_test_8/Cargo.toml
Executable file
8
learn_test_8/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "learn_test_8"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
126
learn_test_8/src/main.rs
Executable file
126
learn_test_8/src/main.rs
Executable file
|
@ -0,0 +1,126 @@
|
|||
use std::{collections::HashMap, fmt::Debug};
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
|
||||
sta_array();
|
||||
pig_latin();
|
||||
company_empolyer_mgn()
|
||||
}
|
||||
|
||||
|
||||
fn sta_array () {
|
||||
let numbers = vec![-6,2,2,4,5,6,6,100,8,20,10];
|
||||
let mut avg = 0;
|
||||
//统计平均値
|
||||
for num in &numbers {
|
||||
avg = (avg + num) / 2;
|
||||
}
|
||||
println!("the average of {:?} is {}", numbers, avg);
|
||||
|
||||
//统计出现次数最多的数字
|
||||
let mut map = HashMap::new();
|
||||
#[derive(Debug)]
|
||||
struct MaxNumInfo{
|
||||
num:i32,
|
||||
cnt:i32,
|
||||
}
|
||||
let mut max_num = MaxNumInfo {
|
||||
num:0,
|
||||
cnt:0,
|
||||
};
|
||||
impl MaxNumInfo{
|
||||
fn from(other:&MaxNumInfo) -> MaxNumInfo{
|
||||
MaxNumInfo{
|
||||
num: other.num,
|
||||
cnt: other.cnt,
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut max_numv: Vec<MaxNumInfo> = Vec::new();
|
||||
for num in &numbers {
|
||||
let cnt = map.entry(num).or_insert(0);
|
||||
*cnt += 1;
|
||||
if *cnt >= max_num.cnt {
|
||||
if *cnt > max_num.cnt {
|
||||
max_numv.clear();
|
||||
}
|
||||
max_num.cnt = *cnt;
|
||||
max_num.num = *num;
|
||||
max_numv.push(MaxNumInfo::from(&max_num));
|
||||
}
|
||||
}
|
||||
println!("the max count in {:?} is {:?}", numbers, max_numv);
|
||||
|
||||
//统计中位数
|
||||
let mut order = numbers.clone();
|
||||
order.sort();
|
||||
let size = order.len() / 2;
|
||||
let num = order[size];
|
||||
println!("median of {:?} is {:?}", numbers, num);
|
||||
|
||||
}
|
||||
|
||||
|
||||
fn pig_latin () {
|
||||
let vowel = vec!['a','e','i','o','u'];
|
||||
let words = vec!["apple", "first"];
|
||||
let mut new_words = Vec::new();
|
||||
for word in &words {
|
||||
for v in &vowel {
|
||||
if let Some(first_char) = word.chars().next() {
|
||||
if first_char == *v {
|
||||
new_words.push(String::from(*word) + "-hay");
|
||||
} else {
|
||||
new_words.push(String::from(&(*word)[1..]) + "-" + &first_char.to_string() + "ay");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("after process {:?} to {:?}", words, new_words);
|
||||
}
|
||||
|
||||
fn company_empolyer_mgn() {
|
||||
struct empolyer_mgn{
|
||||
map:HashMap<String,Vec<String>>,
|
||||
}
|
||||
impl empolyer_mgn {
|
||||
fn new() -> empolyer_mgn {
|
||||
empolyer_mgn {
|
||||
map:HashMap::new(),
|
||||
}
|
||||
}
|
||||
fn add(&mut self, depart:String, name:String) {
|
||||
let employers = self.map.entry(depart).or_insert( Vec::new());
|
||||
employers.push(name);
|
||||
}
|
||||
fn get_all_emplyer(&mut self) {
|
||||
print!("total empolyer is ");
|
||||
for (key, value) in self.map.iter() {
|
||||
print!("{:?} ", value);
|
||||
}
|
||||
println!("");
|
||||
}
|
||||
fn get_emplyer(&mut self, depart:String) {
|
||||
match self.map.entry(depart) {
|
||||
std::collections::hash_map::Entry::Occupied(entry) => {
|
||||
println!("in {} empolyer is {:?}", entry.key(), entry.get());
|
||||
},
|
||||
std::collections::hash_map::Entry::Vacant(entry) => {
|
||||
println!("in {} has no emplyer found", entry.key())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut mgn = empolyer_mgn::new();
|
||||
mgn.add(String::from("项目部门"), String::from("Sally"));
|
||||
mgn.add(String::from("销售部门"), String::from("Amir"));
|
||||
|
||||
mgn.get_all_emplyer();
|
||||
mgn.get_emplyer(String::from("研发部门"));
|
||||
mgn.get_emplyer(String::from("销售部门"));
|
||||
mgn.get_emplyer(String::from("项目部门"));
|
||||
|
||||
}
|
1
life_cycle/.gitignore
vendored
Executable file
1
life_cycle/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
life_cycle/Cargo.toml
Executable file
8
life_cycle/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "life_cycle"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
83
life_cycle/src/main.rs
Executable file
83
life_cycle/src/main.rs
Executable file
|
@ -0,0 +1,83 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
life_cycle();
|
||||
}
|
||||
|
||||
fn life_cycle() {
|
||||
{
|
||||
// 这样定义会使得编译器无法识别返回的数据是x还是无法知道返回的这个借用的生命周期是多少,也就无法防止悬垂引用
|
||||
//fn longest(x:&str, y:&str) -> &str {
|
||||
/* 生命周期是泛型参数的一种,可以像使用泛型参数那样使用生命周期,他的语法是单引号+一个小写字母,生命周期标注加在&之后。
|
||||
返回的引用的生命周期是取决于较小生命周期的那个引用的生命周期:
|
||||
如下的例子,x和y的生命周期都是'a, 但是实际传入的x和y的生命周期并不是一样的,所以'a 代表的生命周期是x和y中较小的一个,
|
||||
这样返回的引用的生命周期也就变得是更小的那个,这样无论返回的是x还是y都能够保证有效
|
||||
*/
|
||||
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
|
||||
if x.len() > y.len() {
|
||||
x
|
||||
} else {
|
||||
y
|
||||
}
|
||||
}
|
||||
{
|
||||
//test 1
|
||||
let str1 = "str1 ....";
|
||||
let str2 = "str2";
|
||||
let lstr = longest(str1, str2);
|
||||
println!("longest str is {}", lstr);
|
||||
}
|
||||
|
||||
//test 2
|
||||
let str1 = "str1 ....";
|
||||
let lstr;
|
||||
{
|
||||
let t = String::from("str2");
|
||||
/* 如果是一个字符串字面值,那么就没有问题。因为他是static的生命周期 */
|
||||
let str2: &str = "str2";
|
||||
|
||||
/* 如果是一个字符串引用那就不行。因为字符串t会在大括号之后生命结束了,其引用也将失效,
|
||||
而打印语句会在大括号外进行longest的结果打印,那么有可能打印的是这个临时字符串的引用,就会导致悬垂引用,所以编译器就会报错,
|
||||
即使在这个实例中str1才是真正的返回的引用,实际运行并不会崩溃。但是编译器并不知道这个信息。他只从最短的生命周期去评估该代码的风险!
|
||||
通过这种方式迫使你编写必不能导致悬垂引用的代码,因为保不齐那天稍微修改了str1和str2的字符串长度。
|
||||
*/
|
||||
//let str2: &str = t.as_str();
|
||||
lstr = longest(str1, str2);
|
||||
}
|
||||
println!("longest str is {}", lstr);
|
||||
}
|
||||
{
|
||||
// 这里固定返回x,将生命周期的标注放在x和返回值上,
|
||||
// 这里必须标注生命周期,因为虽然我们知道返回值是x,但是编译器并不会分析代码的执行,
|
||||
// 所以这里必须显式的标注x和返回值的生命周期
|
||||
fn longest<'a>(x: &'a str, y: &str) -> &'a str {
|
||||
x
|
||||
}
|
||||
let str1 = "str1 ....";
|
||||
let lstr;
|
||||
{
|
||||
let t = String::from("str2");
|
||||
/* 因为longest的返回值的生命周期固定和x相同,
|
||||
那么这里即使传入的y是一个临时的字符串的引用也不会有问题。
|
||||
*/
|
||||
let str2: &str = t.as_str();
|
||||
lstr = longest(str1, str2);
|
||||
// 但是如果将str2传入x那么就会报错,因为str2的生命周期不足以在大括号外使用
|
||||
//lstr = longest(str2, str1);
|
||||
}
|
||||
println!("longest str is {}", lstr);
|
||||
}
|
||||
{
|
||||
#[derive(Debug)]
|
||||
struct test<'a, T> {
|
||||
str1: &'a T,
|
||||
}
|
||||
|
||||
let var;
|
||||
{
|
||||
let t = String::from("str1");
|
||||
var = test { str1: t.as_str() }; // 使用t的字符串引用
|
||||
println!("test: {:?}", var);
|
||||
}
|
||||
//println!("test: {:?}", var); // 在t的生命周期结束后在使用就导致报错
|
||||
}
|
||||
}
|
1
loop_while_for/.gitignore
vendored
Executable file
1
loop_while_for/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
loop_while_for/Cargo.toml
Executable file
8
loop_while_for/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "loop_while_for"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
114
loop_while_for/src/main.rs
Executable file
114
loop_while_for/src/main.rs
Executable file
|
@ -0,0 +1,114 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
loop_fun();
|
||||
while_fun();
|
||||
for_fun();
|
||||
}
|
||||
/* rust 提供了三种循环方式
|
||||
loop、while、和for
|
||||
*/
|
||||
fn loop_fun(){
|
||||
//loop 循环,一直循环直到主动退出
|
||||
//loop 从功能上和while true 是一样的,
|
||||
//但是为什么说要单独列出来我目前还不是很清楚
|
||||
|
||||
let mut num = 100;
|
||||
loop {
|
||||
// num --;// 不允许这样写
|
||||
num -= 1;
|
||||
if num > 10{
|
||||
//提前结束本次循环
|
||||
continue;
|
||||
}
|
||||
if num < 0 {
|
||||
// 主动结束循环
|
||||
break;
|
||||
}
|
||||
println!("{}", num)
|
||||
} // 这里是不需要加上分号的,因为他不是一个语句
|
||||
println!("go,go,go");
|
||||
|
||||
// loop也是表达式,所以其可以产生值作为右值
|
||||
let mut num = 100;
|
||||
let cond = 3%num;
|
||||
let ret = loop {
|
||||
num -=1;
|
||||
if cond == num{
|
||||
//break;//这样主动退出之后ret的值将会是一个()
|
||||
//break //这样也是可以的和上面效果一致
|
||||
|
||||
break num; //case1 这样会把num作为整个loop表达式的值给到ret, ret类型就是i32
|
||||
//break num //case2 不加上分号也是可以的
|
||||
/* case3 可以换行写,但是要加分号了,不然跨了行不会被认为是一个完整的语句,
|
||||
但是还是不要写这变态的代码吧😅
|
||||
break
|
||||
num;
|
||||
*/
|
||||
|
||||
break 123;//break语句后的代码不会被执行到。
|
||||
}
|
||||
};
|
||||
println!("ret is {}", ret)
|
||||
}
|
||||
|
||||
fn while_fun(){
|
||||
// while 循环,
|
||||
// 检查条件如果符合就进入循环并直到主动退出或者条件不满足
|
||||
// 每轮循环都会检查条件
|
||||
|
||||
let mut num = 30;
|
||||
// 循环条件的检查,满足才会开始&继续循环
|
||||
while num > 0 {
|
||||
if num > 10{
|
||||
println!("{} is > 10, I cant process -_-!", num);
|
||||
break; //提前终止循环
|
||||
}
|
||||
if num % 2 == 0 {
|
||||
println!("{}!", num);
|
||||
num -= 1;
|
||||
continue;// 提前终止本轮循环
|
||||
}
|
||||
println!("{}", num);
|
||||
num -= 1;//修改循环条件
|
||||
} // 这里同样不用加分号,因为while 是表达式不是语句
|
||||
if num == 0{
|
||||
println!("go,go,go");
|
||||
}
|
||||
|
||||
let mut num = 30;
|
||||
// while 表达式可作为右值,但是在里面不能用break返回其值
|
||||
// 因为while有可能是条件不满足而结束,此时是没有值的,如果break指定了返回值,这样就会导致
|
||||
// 编译器无法在编译期间确定ret的类型!
|
||||
// 这也引发了我对loop的思考,是否就是while这种特性,导致需要一种完全交给开发者可控的退出方式的循环才催生了loop,👻
|
||||
// 因为loop 的退出是完全开发者编写的退出时机,也就使得可以带有返回值
|
||||
let ret = while num > 0 {
|
||||
num = 0;
|
||||
//break num;//不可以像loop那样带有值
|
||||
break;
|
||||
}; // 这里必须加分号,因为while 表达式作为右值和let ret一起构成完整语句
|
||||
}
|
||||
|
||||
fn for_fun(){
|
||||
//for 循环 语法就是 for xxx in yyy {}
|
||||
// yyy是一个可迭代的对象,比如数组,range等
|
||||
// for 循环相比较while,loop可以安全的遍历迭代对象而不用担心越界的问题。
|
||||
|
||||
let ary = [0, 1, 2, 3, 4, 5];
|
||||
|
||||
//遍历整个数组并打印
|
||||
for item in ary.iter() {
|
||||
println!("{}", item);
|
||||
}
|
||||
|
||||
//从100到-1遍历一遍 range: start..end
|
||||
let rng = 100..1;
|
||||
for item in 100..-1 {
|
||||
if item > 10 {
|
||||
continue;
|
||||
}
|
||||
if item <=0 {
|
||||
break;// 和while 一样可以提前退出但是无法携带值
|
||||
}
|
||||
println!("{}", item);
|
||||
}
|
||||
}
|
1
minigrep/.gitignore
vendored
Executable file
1
minigrep/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
minigrep/Cargo.toml
Executable file
8
minigrep/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "minigrep"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
106
minigrep/src/lib.rs
Executable file
106
minigrep/src/lib.rs
Executable file
|
@ -0,0 +1,106 @@
|
|||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
pub struct Config {
|
||||
pub query: String,
|
||||
pub filename: String,
|
||||
pub sensitive: bool,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new(args: &[String]) -> Result<Config, &'static str> {
|
||||
if args.len() < 2 {
|
||||
return Err("args len less then 2");
|
||||
}
|
||||
let query = args[0].clone();
|
||||
let filename = args[1].clone();
|
||||
let sensitive = std::env::var("CASE_SENSITIVE").is_ok();
|
||||
Ok(Config {
|
||||
query,
|
||||
filename,
|
||||
sensitive,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GrepRet {
|
||||
line: i32,
|
||||
context: String,
|
||||
}
|
||||
impl GrepRet {
|
||||
fn new(line: i32, context: &str) -> GrepRet {
|
||||
GrepRet {
|
||||
line,
|
||||
context: context.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl fmt::Display for GrepRet {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "line:{}: {}", self.line, self.context)
|
||||
}
|
||||
}
|
||||
pub fn run(config: &Config) -> Result<Vec<GrepRet>, Box<dyn Error>> {
|
||||
let context = std::fs::read_to_string(&config.filename)?;
|
||||
if config.sensitive {
|
||||
Ok(search_sensitive(&config.query, &context))
|
||||
} else {
|
||||
Ok(search_insensitive(&config.query, &context))
|
||||
}
|
||||
}
|
||||
|
||||
fn search_sensitive(query: &str, context: &String) -> Vec<GrepRet> {
|
||||
let mut idx = 0;
|
||||
let mut result = Vec::new();
|
||||
for line in context.lines() {
|
||||
idx += 1;
|
||||
if line.contains(query) {
|
||||
result.push(GrepRet::new(idx, line));
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn search_insensitive(query: &str, context: &String) -> Vec<GrepRet> {
|
||||
let mut idx = 0;
|
||||
let mut result = Vec::new();
|
||||
for line in context.lines() {
|
||||
idx += 1;
|
||||
let lower = line.to_lowercase();
|
||||
if lower.contains(&query.to_lowercase()) {
|
||||
result.push(GrepRet::new(idx, line));
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn grep_new1() {
|
||||
let line = 10;
|
||||
let context = "zzz";
|
||||
let grep_ret = GrepRet::new(line, context);
|
||||
assert_eq!(line, grep_ret.line);
|
||||
assert_eq!(context, grep_ret.context);
|
||||
}
|
||||
#[test]
|
||||
fn grep_new2() {
|
||||
let line = 10;
|
||||
let context = "zzz";
|
||||
let grep_ret = GrepRet::new(line, context);
|
||||
assert_ne!(!line, grep_ret.line);
|
||||
assert_eq!(context, grep_ret.context);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // cargo test -- --ignored
|
||||
fn grep_new3() {
|
||||
let line = 10;
|
||||
let context = "zzz";
|
||||
let grep_ret = GrepRet::new(line, context);
|
||||
assert_ne!(!line, grep_ret.line);
|
||||
assert_eq!(context, grep_ret.context);
|
||||
}
|
||||
}
|
25
minigrep/src/main.rs
Executable file
25
minigrep/src/main.rs
Executable file
|
@ -0,0 +1,25 @@
|
|||
use std::env;
|
||||
use std::process;
|
||||
|
||||
use minigrep;
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() < 3 {
|
||||
eprintln!("Usage: {} <text> <input file>", &args[0]);
|
||||
return;
|
||||
}
|
||||
let args = &args[1..];
|
||||
let config = minigrep::Config::new(args).unwrap_or_else(|err| {
|
||||
println!("Problem: {}", err);
|
||||
process::exit(-1);
|
||||
});
|
||||
let result = minigrep::run(&config).unwrap_or_else(|err| {
|
||||
println!("Problem: {}", err);
|
||||
process::exit(-1);
|
||||
});
|
||||
println!("find {} in {}, is:", config.query, config.filename);
|
||||
for it in result {
|
||||
println!("{}", it);
|
||||
}
|
||||
}
|
9
minigrep/test.txt
Executable file
9
minigrep/test.txt
Executable file
|
@ -0,0 +1,9 @@
|
|||
ekko.bao
|
||||
emma.wang
|
||||
wwww
|
||||
to
|
||||
rust
|
||||
rust
|
||||
today is a nice day
|
||||
to you beast
|
||||
EKKO
|
9
minigrep/tests/intergation.rs
Executable file
9
minigrep/tests/intergation.rs
Executable file
|
@ -0,0 +1,9 @@
|
|||
use minigrep;
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn grep_new2() {
|
||||
let args = vec![String::from("zzz"), String::from("zzz.txt")];
|
||||
let config = minigrep::Config::new(&args).unwrap();
|
||||
let _ = minigrep::run(&config).unwrap();
|
||||
}
|
1
minigrep_iterator/.gitignore
vendored
Executable file
1
minigrep_iterator/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
minigrep_iterator/Cargo.toml
Executable file
8
minigrep_iterator/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "minigrep"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
132
minigrep_iterator/src/lib.rs
Executable file
132
minigrep_iterator/src/lib.rs
Executable file
|
@ -0,0 +1,132 @@
|
|||
//! mini grep
|
||||
//! 实现一个简易版本的grep工具的功能,通过传入要查询的字符串和文件名,会遍历每一行文件内容并将其输出到终端
|
||||
//!
|
||||
//! ***注意***
|
||||
//! 可通过 CASE_SENSITIVE 环境变量来进行设定是否忽略大小写进行匹配。
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
pub struct Config {
|
||||
/// 需要查询的字符串
|
||||
pub query: String,
|
||||
/// 检索字符串的文件名
|
||||
pub filename: String,
|
||||
/// 是否大小写敏感
|
||||
/// - true: 大小写敏感
|
||||
/// - false: 忽略大小写
|
||||
pub sensitive: bool,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Config 的构造函数,传入一个string的iter,会将其构造为一个Config并返回,如果失败则返回一个Err并带有说明的字符串
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use minigrep::Config;
|
||||
/// let mut args = vec![String::from("to"), String::from("test.txt")];
|
||||
/// let cfg = Config::new(args.into_iter()).unwrap();
|
||||
/// assert_eq!(cfg.query, "to");
|
||||
/// assert_eq!(cfg.filename, "test.txt");
|
||||
/// ```
|
||||
pub fn new<T>(mut args: T) -> Result<Config, &'static str>
|
||||
where
|
||||
T: Iterator<Item = String>,
|
||||
{
|
||||
let query = args.next().unwrap();
|
||||
let filename = args.next().unwrap();
|
||||
let sensitive = std::env::var("CASE_SENSITIVE").is_ok();
|
||||
Ok(Config {
|
||||
query,
|
||||
filename,
|
||||
sensitive,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GrepRet {
|
||||
line: i32,
|
||||
context: String,
|
||||
}
|
||||
impl GrepRet {
|
||||
fn new(line: i32, context: &str) -> GrepRet {
|
||||
GrepRet {
|
||||
line,
|
||||
context: context.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl fmt::Display for GrepRet {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "line:{}: {}", self.line, self.context)
|
||||
}
|
||||
}
|
||||
/// 字符串匹配的实操入口函数,
|
||||
/// - 入参:<br>
|
||||
/// Config
|
||||
/// - 返回值:<br>
|
||||
/// 查询到的匹配字串的文本行以及行号,如果发生错误则返回Err
|
||||
pub fn run(config: &Config) -> Result<Vec<GrepRet>, Box<dyn Error>> {
|
||||
let context = std::fs::read_to_string(&config.filename)?;
|
||||
if config.sensitive {
|
||||
Ok(search_sensitive(&config.query, &context))
|
||||
} else {
|
||||
Ok(search_insensitive(&config.query, &context))
|
||||
}
|
||||
}
|
||||
|
||||
fn search_sensitive(query: &str, context: &String) -> Vec<GrepRet> {
|
||||
let mut idx = 0;
|
||||
let mut result = Vec::new();
|
||||
for line in context.lines() {
|
||||
idx += 1;
|
||||
if line.contains(query) {
|
||||
result.push(GrepRet::new(idx, line));
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn search_insensitive(query: &str, context: &String) -> Vec<GrepRet> {
|
||||
let mut idx = 0;
|
||||
let mut result = Vec::new();
|
||||
for line in context.lines() {
|
||||
idx += 1;
|
||||
let lower = line.to_lowercase();
|
||||
if lower.contains(&query.to_lowercase()) {
|
||||
result.push(GrepRet::new(idx, line));
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn grep_new1() {
|
||||
let line = 10;
|
||||
let context = "zzz";
|
||||
let grep_ret = GrepRet::new(line, context);
|
||||
assert_eq!(line, grep_ret.line);
|
||||
assert_eq!(context, grep_ret.context);
|
||||
}
|
||||
#[test]
|
||||
fn grep_new2() {
|
||||
let line = 10;
|
||||
let context = "zzz";
|
||||
let grep_ret = GrepRet::new(line, context);
|
||||
assert_ne!(!line, grep_ret.line);
|
||||
assert_eq!(context, grep_ret.context);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // cargo test -- --ignored
|
||||
fn grep_new3() {
|
||||
let line = 10;
|
||||
let context = "zzz";
|
||||
let grep_ret = GrepRet::new(line, context);
|
||||
assert_ne!(!line, grep_ret.line);
|
||||
assert_eq!(context, grep_ret.context);
|
||||
}
|
||||
}
|
25
minigrep_iterator/src/main.rs
Executable file
25
minigrep_iterator/src/main.rs
Executable file
|
@ -0,0 +1,25 @@
|
|||
use std::env;
|
||||
use std::process;
|
||||
|
||||
use minigrep;
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() < 3 {
|
||||
eprintln!("Usage: {} <text> <input file>", &args[0]);
|
||||
return;
|
||||
}
|
||||
let args1 = args.into_iter().skip(1);
|
||||
let config = minigrep::Config::new(args1).unwrap_or_else(|err| {
|
||||
println!("Problem: {}", err);
|
||||
process::exit(-1);
|
||||
});
|
||||
let result = minigrep::run(&config).unwrap_or_else(|err| {
|
||||
println!("Problem: {}", err);
|
||||
process::exit(-1);
|
||||
});
|
||||
println!("find {} in {}, is:", config.query, config.filename);
|
||||
for it in result {
|
||||
println!("{}", it);
|
||||
}
|
||||
}
|
9
minigrep_iterator/test.txt
Executable file
9
minigrep_iterator/test.txt
Executable file
|
@ -0,0 +1,9 @@
|
|||
ekko.bao
|
||||
emma.wang
|
||||
wwww
|
||||
to
|
||||
rust
|
||||
rust
|
||||
today is a nice day
|
||||
to you beast
|
||||
EKKO
|
1
module_learn/.gitignore
vendored
Executable file
1
module_learn/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
module_learn/Cargo.toml
Executable file
8
module_learn/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "module_learn"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
7
module_learn/src/bin/app1.rs
Executable file
7
module_learn/src/bin/app1.rs
Executable file
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
rust 可以有多个二进制的crate,目录都是在src/bin 目录下。每个rs就是一个二进制包文件
|
||||
比如这里就是二进制crate, app1,使用cargo build --bin app1 即可进行编译
|
||||
*/
|
||||
fn main() {
|
||||
println!("Hello, world! app1");
|
||||
}
|
3
module_learn/src/bin/app2.rs
Executable file
3
module_learn/src/bin/app2.rs
Executable file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
println!("Hello, world! app2");
|
||||
}
|
132
module_learn/src/lib.rs
Executable file
132
module_learn/src/lib.rs
Executable file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
rust 只可以有一个library的单元包,文件的路径必须是src/lib.rs。包名就是cargo new的时候创建的工程名。
|
||||
可以使用 cargo build --lib 进行编译
|
||||
*/
|
||||
|
||||
//该模块位public的可以被外部的code进行导入使用
|
||||
pub mod level0_mod1{
|
||||
//定义一个public的函数,可以暴露给外部调用
|
||||
pub fn func(){
|
||||
println!("This is level0_mod1::func");
|
||||
//fun1属于本模块内的私有函数。模块内的code 是可以使用的,
|
||||
fun1();
|
||||
levl1_mod1::func();
|
||||
//levl1_mod1::fun1是私有的,只能在levl1_mod1 子模块内使用。
|
||||
//levl1_mod1::fun1();
|
||||
|
||||
//levl2_mod1 是私有的只能在 levl1_mod2 模块内使用。不能被更外部的code使用
|
||||
//levl1_mod2::levl2_mod1::func();
|
||||
}
|
||||
//定义一个私有的函数。外部不能直接使用,只能模块内部的code可以使用
|
||||
fn fun1() {
|
||||
println!("This is level0_mod1::func1");
|
||||
levl1_mod1::func();
|
||||
}
|
||||
|
||||
//定义一个私有的模块,。只能被同级的其他模块及其子模块调用,不能被外部的code调用
|
||||
mod levl1_mod1 {
|
||||
pub fn func(){
|
||||
println!("This is levl1_mod1::func");
|
||||
self::fun1();
|
||||
}
|
||||
//这一一个私有模块中的私有函数。只能被同模块下的其他code调用,不能被本模块外的code调用
|
||||
fn fun1() {
|
||||
println!("This is levl1_mod1::func1");
|
||||
}
|
||||
}
|
||||
|
||||
//定义一个公共的子模块。由于其父模块也是public的所以其可以被外部的code进行调用
|
||||
pub mod levl1_mod2 {
|
||||
pub fn func() {
|
||||
println!("This is levl1_mod2::func");
|
||||
levl2_mod1::func();
|
||||
}
|
||||
mod levl2_mod1 {
|
||||
pub fn func(){
|
||||
println!("This is levl2_mod1::func");
|
||||
//可以使用绝对路径调用函数。根目录就是creat关键字,相对于文件系统目录里面/
|
||||
crate::level0_mod1::levl1_mod1::func();
|
||||
//也可以使用相对路径进行调用
|
||||
//self 相当于文件系统里面的 . 代表当前目录,也就是当前模块
|
||||
//super 相当于文件系统里面的 .. 代表当前目录里的上一级目录,也就是父级模块
|
||||
self::super::super::levl1_mod1::func();
|
||||
//这个函数是 levl1_mod1 中私有的不能跨mod使用
|
||||
//crate::level0_mod1::levl1_mod1::fun1();
|
||||
//这样也是ok的,表示同级模块下的其他函数
|
||||
self::func1();
|
||||
}
|
||||
fn func1(){
|
||||
println!("This is levl2_mod1::func1");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
模块和里面的函数,数据等默认都是私有的,如果要暴露给外部使用则需要加上pub关键字
|
||||
如果是模块私有的,则只能被同模块或者子模块进行调用,不能被外部调用
|
||||
如果函数是私有的那么只能被同模块下的code进行调用。即使其所属模块是公共的,其他和所述模块同级的模块也不能使用该函数,
|
||||
总之,如果是私有的就是不能被跨模块被使用。但是对于所有的本模块内的成员来说都是公共的。
|
||||
所以:
|
||||
如果我们定义一个模块,像提供一些功能给外部使用,那么可以把这些API都定义到一个公共的mod中,
|
||||
其他具体的实现可以放在另外的私有模块中,然后在这个私有模块里将其具体的实现设置为pub的,这样就可以只让暴露到外部的API的所述模块
|
||||
调用到这里具体的实现而进行实作,但是外部无法使用。
|
||||
*/
|
||||
|
||||
//本模块的函数
|
||||
pub fn func(){
|
||||
println!("This is level0_mod1::func");
|
||||
}
|
||||
|
||||
//定义一个结构体,并加上pub关键字使其暴露给外部使用。
|
||||
pub struct struct1{
|
||||
/* 即使结构体是pub的但是不加pub的成员依然是私有的 */
|
||||
pub member1: i32, //该member是公有的外部可以访问到
|
||||
member2:i32, //该member是私有的。外部无法访问到
|
||||
}
|
||||
|
||||
impl struct1{
|
||||
//公共的函数,可以被外部使用
|
||||
pub fn func(){
|
||||
println!("This is struct1->func");
|
||||
}
|
||||
//私有的函数,不可以被外部使用
|
||||
fn func1(){
|
||||
println!("This is struct1->func1");
|
||||
}
|
||||
}
|
||||
|
||||
//定义一个枚举,并加上pub关键字使其暴露给外部使用。
|
||||
//对于枚举来说 和模块以及struct不同,只要枚举是pub的那么其变体也都是pub的
|
||||
//因为只有只有所有变体都是pub的才能起到最大作用
|
||||
pub enum enum1{
|
||||
member1, //该member是公有的外部可以访问到
|
||||
member2, //该member是公有的外部可以访问到
|
||||
}
|
||||
|
||||
impl enum1 {
|
||||
//公共的函数,可以被外部使用
|
||||
pub fn func(){
|
||||
println!("This is struct1->func");
|
||||
}
|
||||
//私有的函数,不可以被外部使用
|
||||
fn func1(){
|
||||
println!("This is struct1->func1");
|
||||
}
|
||||
}
|
||||
|
||||
/* 使用文件目录的形式拆分module,以便于减小单个文件的代码量
|
||||
在lib.rs 里面定义模块名,在同级目录建立module同名的rs文件。为该module的实现
|
||||
如果module还存在子module,那就再建立和module同名的文件夹,在文件夹里面建立sub_module.rs存放sub_module的实现
|
||||
如此往复形成树状结构的代码源码目录。
|
||||
├─src
|
||||
│ ├─bin #二进制单元包, 可以有多个,每个单独为一个rs文件
|
||||
│ │ ├─app1.rs
|
||||
│ │ └─app2.rs
|
||||
│ ├─lib.rs #library 单元包 只能有一个,入口必须是lib.rs
|
||||
│ ├─tree0_mod1.rs #module tree0_mod1
|
||||
│ └─tree0_mod1 #所有 module tree0_mod1 的子module的实现源码存放目录
|
||||
│ ├─tree1_mod2.rs #module tree1_mod2 ,其为 tree0_mod1 的子module
|
||||
│ └─tree1_mod2 #所有 module tree1_mod2 的子module的实现源码存放目录
|
||||
│ └─tree2_mod1.rs #module tree2_mod1,其为 tree1_mod2 的子module
|
||||
*/
|
||||
pub mod tree0_mod1;
|
21
module_learn/src/main.rs
Executable file
21
module_learn/src/main.rs
Executable file
|
@ -0,0 +1,21 @@
|
|||
//使用模块路径进行导入, module_learn 代表的是这个lib的名字。 level0_mod1代表是这个lib下的模块
|
||||
use module_learn::level0_mod1;
|
||||
//使用{}可以一次性导入多个模块/函数等,不必多行
|
||||
use module_learn::level0_mod1::{levl1_mod2, func};
|
||||
use module_learn;
|
||||
|
||||
use module_learn::tree0_mod1;
|
||||
//如果导入时存在重名的,可以使用as关键字起一个别名去辨别。
|
||||
use std::io::Result as ioResult;
|
||||
use std::fmt::Result as fmtResult;
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
level0_mod1::func();
|
||||
func();
|
||||
levl1_mod2::func();
|
||||
|
||||
module_learn::func();
|
||||
|
||||
tree0_mod1::func();
|
||||
}
|
23
module_learn/src/tree0_mod1.rs
Executable file
23
module_learn/src/tree0_mod1.rs
Executable file
|
@ -0,0 +1,23 @@
|
|||
//定义一个public的函数,可以暴露给外部调用
|
||||
pub fn func(){
|
||||
println!("This is tree0_mod1::func");
|
||||
//fun1属于本模块内的私有函数。模块内的code 是可以使用的,
|
||||
fun1();
|
||||
tree1_mod1::func();
|
||||
//tree1_mod1::fun1是私有的,只能在tree1_mod1 子模块内使用。
|
||||
//tree1_mod1::fun1();
|
||||
|
||||
//tree2_mod1 是私有的只能在 tree1_mod2 模块内使用。不能被更外部的code使用
|
||||
//tree1_mod2::tree2_mod1::func();
|
||||
}
|
||||
//定义一个私有的函数。外部不能直接使用,只能模块内部的code可以使用
|
||||
fn fun1() {
|
||||
println!("This is tree0_mod1::func1");
|
||||
tree1_mod1::func();
|
||||
}
|
||||
|
||||
//定义一个私有的模块,。只能被同级的其他模块及其子模块调用,不能被外部的code调用
|
||||
mod tree1_mod1;
|
||||
|
||||
//定义一个公共的子模块。由于其父模块也是public的所以其可以被外部的code进行调用
|
||||
pub mod tree1_mod2;
|
8
module_learn/src/tree0_mod1/tree1_mod1.rs
Executable file
8
module_learn/src/tree0_mod1/tree1_mod1.rs
Executable file
|
@ -0,0 +1,8 @@
|
|||
pub fn func(){
|
||||
println!("This is tree1_mod1::func");
|
||||
self::fun1();
|
||||
}
|
||||
//这一一个私有模块中的私有函数。只能被同模块下的其他code调用,不能被本模块外的code调用
|
||||
fn fun1() {
|
||||
println!("This is tree1_mod1::func1");
|
||||
}
|
5
module_learn/src/tree0_mod1/tree1_mod2.rs
Executable file
5
module_learn/src/tree0_mod1/tree1_mod2.rs
Executable file
|
@ -0,0 +1,5 @@
|
|||
pub fn func() {
|
||||
println!("This is tree1_mod2::func");
|
||||
tree2_mod1::func();
|
||||
}
|
||||
mod tree2_mod1;
|
16
module_learn/src/tree0_mod1/tree1_mod2/tree2_mod1.rs
Executable file
16
module_learn/src/tree0_mod1/tree1_mod2/tree2_mod1.rs
Executable file
|
@ -0,0 +1,16 @@
|
|||
pub fn func(){
|
||||
println!("This is tree2_mod1::func");
|
||||
//可以使用绝对路径调用函数。根目录就是creat关键字,相对于文件系统目录里面/
|
||||
crate::tree0_mod1::tree1_mod1::func();
|
||||
//也可以使用相对路径进行调用
|
||||
//self 相当于文件系统里面的 . 代表当前目录,也就是当前模块
|
||||
//super 相当于文件系统里面的 .. 代表当前目录里的上一级目录,也就是父级模块
|
||||
self::super::super::tree1_mod1::func();
|
||||
//这个函数是 tree1_mod1 中私有的不能跨mod使用
|
||||
//crate::level0_mod1::tree1_mod1::fun1();
|
||||
//这样也是ok的,表示同级模块下的其他函数
|
||||
self::func1();
|
||||
}
|
||||
fn func1(){
|
||||
println!("This is tree2_mod1::func1");
|
||||
}
|
1
ownership/.gitignore
vendored
Executable file
1
ownership/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
ownership/Cargo.toml
Executable file
8
ownership/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "ownership"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
146
ownership/src/main.rs
Executable file
146
ownership/src/main.rs
Executable file
|
@ -0,0 +1,146 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
ownership1();
|
||||
ownership2();
|
||||
ownership3();
|
||||
ownership4();
|
||||
ownership5();
|
||||
}
|
||||
|
||||
// rust 所有权系统
|
||||
// Rust 使用所有权系统来管理来自heap上的内存,
|
||||
// 1. 减少数据竞争,实现类似读写锁的机制
|
||||
// 2. 避免提前free,double free,悬垂引用等内存问题。
|
||||
fn ownership1(){
|
||||
/* 对于实现了Copy trait 的类型,rust会在赋值的时候直接copy一份给到新的变量,
|
||||
* 这样使得新的变量和现有的变量可同时使用,互不影响
|
||||
*/
|
||||
//将45绑定到num上
|
||||
let num = 45;
|
||||
//使用i32的Copy 将值拷贝并绑定到num1上
|
||||
let num1 = num;
|
||||
println!("num is {}, num1 is {}", num, num1);
|
||||
/* 实现了Copy trait的类型有:
|
||||
* 1. i32 u32 f32 char 等基本数据类型
|
||||
* 因为这些类型简单,往往都是存在stack上的,基本不会因为Copy trait影响性能
|
||||
* 但是如果是复杂的类型。比如一个动态可以变化的数组,字符串那么其如果每赋值一次都完整copy性能损失过大,
|
||||
* 所以其一般在赋值时都只是将地址和一些长度信息进行赋值给新变量,而内容都指向同一块内存地址。
|
||||
* 这样就会导致如果一个修改了里面的内容或者释放了这段内存,另外一个变量的访问将变得特别危险。
|
||||
*/
|
||||
}
|
||||
|
||||
fn ownership2(){
|
||||
/* 对于没有实现Copy trait 的类型: 同一时间只能归属于一个变量,
|
||||
所以当使用一个变量赋值给另外一个变量的时候前一个变量的就被shadow掉了,无法再使用,
|
||||
如果再使用就会编译出错。
|
||||
*/
|
||||
let mut str1 = String::new();
|
||||
str1.push_str("This is a string");// 通过str1操作字符串,添加一串字符串到String里
|
||||
let mut str2 = str1; // 将所有权移交给str2
|
||||
//此处String对象的所有权已经从str1移交给了str2,此时已经不能再通过str1去访问(读)或者修改(写)字符串了
|
||||
//str1.push_str("owner is not str1, can access string") //写
|
||||
//println!("{}", str1); //读
|
||||
str2.push_str("owner is str2");
|
||||
println!("{}", str2);
|
||||
|
||||
/* 这里的String就是一个没有copy trait的类型,其会在heap上申请一段内存存放字符串,
|
||||
* 而变量str1或者str2是在stack上的,他们均是记录其相关信息的载体,但是字符串实体还是在heap上。
|
||||
* 如上的示例里面可以看出,rust通过所有权的机制,确保同一时间只能有一个所有者可以操作字符串,
|
||||
* 如果所有权移出去那么就不再有访问的权利,也就存在读写,释放的可能性,
|
||||
* 写代码时始终只需要关注当前所有者的行为即可。
|
||||
*/
|
||||
}
|
||||
|
||||
fn ownership3(){
|
||||
/* 所有权不仅可以移交给变量也可以通过函数参数移交给函数,但是移交给函数有一个使用上的问题:
|
||||
* 移交给函数后,还需要函数通过函数返回值将所有权返回,不然变量的生命周期会随着调用的函数结束而结束
|
||||
*/
|
||||
let mut str1 = String::new();
|
||||
str1.push_str("This is a string");// 通过str1操作字符串,添加一串字符串到String里
|
||||
|
||||
/* opt1: 如下的操作是不被允许的,因为str1 所有权移交给了function join_str,str1已经不可再访问 */
|
||||
//join_str(str1, "sub string");
|
||||
//println!("after join str is {}", str1);
|
||||
/* opt2: 只有像如下的操作将所有权从functon的返回值中再次移交回来给到一个新的变量str1才可访问字符串
|
||||
* 注意这里的str1已经和上面的str1不一样了哦,上面那个str1已经被shadow掉了
|
||||
*/
|
||||
let (sub_len, str1) = join_str(str1, "sub string");
|
||||
println!("after join str is {}", str1);
|
||||
}
|
||||
fn join_str(mut str_src:String, sub_str:&str) -> (usize, String){
|
||||
let len = sub_str.len();
|
||||
str_src.push_str(sub_str);
|
||||
(len,str_src)
|
||||
}
|
||||
|
||||
fn ownership4(){
|
||||
|
||||
/* 像ownership3 所示的例子中的用法会使得复杂的数据类型在使用上会变得很麻烦,
|
||||
* 因为如果一个函数有多个参数并且每个参数都是复杂的数据类型,那么势必会导致
|
||||
* 每个函数的返回值都特别冗余,并且有时候我们所调用的函数只是用一下参数的一些值,并不会改变参数,
|
||||
* 也就是不是要所有权而是只是想使用一下。
|
||||
* 所以rust就有了引用和借用的概念:
|
||||
* 将变量借用给函数或另外一个变量使用。对于原变量就是借用,对于新的变量或者函数就是引用。
|
||||
* */
|
||||
let mut str1 = String::new();
|
||||
str1.push_str("This is a string");// 通过str1操作字符串,添加一串字符串到String里
|
||||
let sub_str = 'A';
|
||||
// 通过 & 将变量借用给has_char
|
||||
let ret = has_char(&str1, sub_str);
|
||||
match ret {
|
||||
//将字符串借用给函数has_char后str1依然用拥有其所有权,依然可以读写访问
|
||||
true =>println!("{} has char {}", str1, sub_str),
|
||||
false =>println!("[{}] has no char [{}]", str1, sub_str),
|
||||
}
|
||||
str1.push_str("add string after borrow");
|
||||
|
||||
/* 可以有多个不可变引用
|
||||
* 引用的变量是不可以修改其内容的,因为借你,你要原样的归还,也就是只能读不能写
|
||||
* 这就天然形成了读写锁
|
||||
*/
|
||||
let str2 = &str1;
|
||||
let str3 = &str1;// 可以有多个不可变引用
|
||||
//str2.push_str("cant add str!"); //❎
|
||||
println!("{} {} {}", str1, str2, str3);//尝试使用他们
|
||||
|
||||
/* 不可以同时有可变引用和不可变应用:
|
||||
* 只能分时复用,不能在同一块代码逻辑里
|
||||
* 我的理解是可变引用和不可变引用,相当于是指向原始变量的指针,当我们通过两个指针(一读一写)去访问原始字串时会导致数据竞争
|
||||
* 所以为了保证数据的一致性,不允许可能发生的同时读写的情况。
|
||||
*/
|
||||
let mut str1 = String::new();
|
||||
str1.push_str("This is a string");// 通过str1操作字符串,添加一串字符串到String里
|
||||
//let str2 = & mut str1;
|
||||
//str2.push_str("add string by borrowed mut str2");
|
||||
//let str3 = &str1; //这里会报错不能同时借用给muteable和immuteable。
|
||||
//println!("{} {} {}", str1, str2, str3); //在这里尝试使用三个字符串。
|
||||
//{
|
||||
// let str3 = &str1;
|
||||
// println!("{} {}", str1, str3); //在这里尝试使用
|
||||
//}
|
||||
{
|
||||
let str2 = & mut str1;
|
||||
str2.push_str("add string by borrowed mut str2");
|
||||
//str1.push_str("adas");// 不能同时读写
|
||||
//println!("{} {}", str1, str2); //在这里尝试使用。
|
||||
}
|
||||
}
|
||||
fn has_char(str_src:&String, sub_str:char) -> bool{
|
||||
let idx = str_src.find(sub_str);
|
||||
if idx == None{
|
||||
false
|
||||
}else{
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn ownership5(){
|
||||
//
|
||||
let mut str1 = String::new();
|
||||
str1.push_str("This is a string");// 通过str1操作字符串,添加一串字符串到String里
|
||||
let str2 = &str1;
|
||||
str1.push_str("add another string");
|
||||
|
||||
}
|
1
p15.smart_point/boxt/.gitignore
vendored
Executable file
1
p15.smart_point/boxt/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
p15.smart_point/boxt/Cargo.toml
Executable file
8
p15.smart_point/boxt/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "boxt"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
32
p15.smart_point/boxt/src/SmartPoint/BoxT.rs
Executable file
32
p15.smart_point/boxt/src/SmartPoint/BoxT.rs
Executable file
|
@ -0,0 +1,32 @@
|
|||
/// # Box<T>
|
||||
/// 可以理解就是void*,其指向存储在堆上的内存,因为RUST必须在编译器知道栈的分配,
|
||||
/// 但是某些数据必须是运行时动态变化的,大小是不确定的,所以可以将其通过box(装箱),
|
||||
/// 使用者在使用时将其拆箱进行使用,但是代码的组织上可以使用大小确定的Box<T>来占位。
|
||||
pub fn learn() {
|
||||
let mut num = Box::new(5);
|
||||
|
||||
*num = 7;
|
||||
println!("num = {}", num);
|
||||
|
||||
/// 构造一个链表,链表的每个节点存储一个i32另外一个元素是另一个节点
|
||||
enum List {
|
||||
//Cons(i32, List), //直接使用List会导致无限递归,无法计算出List的大小
|
||||
Cons(i32, Box<List>), // 使用Box将装箱,使得可以实现该需求。因为Box的大小是确定,
|
||||
Nil,
|
||||
}
|
||||
let mut list = List::Cons(12, Box::new(List::Cons(13, Box::new(List::Nil))));
|
||||
|
||||
loop {
|
||||
list = match list {
|
||||
List::Cons(num, next) => {
|
||||
println!("{},", num);
|
||||
*next
|
||||
}
|
||||
List::Nil => {
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Deref trait(Dereference operator) 解引用运算符,可以通过实现Deref trait来实现在使用上智能指针像是引用一样。
|
||||
}
|
62
p15.smart_point/boxt/src/SmartPoint/my_box.rs
Executable file
62
p15.smart_point/boxt/src/SmartPoint/my_box.rs
Executable file
|
@ -0,0 +1,62 @@
|
|||
/// 实现一个自定义的box类型,效果和Box<T>类似,
|
||||
/// 目的是为了理解Deref的意义
|
||||
struct MyBox<T>(T);
|
||||
|
||||
impl<T> MyBox<T> {
|
||||
pub fn new(item: T) -> MyBox<T> {
|
||||
MyBox(item)
|
||||
}
|
||||
}
|
||||
|
||||
use std::ops::{Deref, DerefMut};
|
||||
/// 手动给MyBox实现一个defref trait
|
||||
impl<T> Deref for MyBox<T> {
|
||||
type Target = T;
|
||||
// 实现解引用,返回MyBox里面包含的数据的引用
|
||||
// 这样当使用*号时候就可以获得MyBox的数据了
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
pub fn learn() {
|
||||
let x = 5;
|
||||
let y = MyBox::new(6);
|
||||
|
||||
println!("X: {}, Y:{}", x, *y); // 当MyBox实现了Deref trait的时候*y即可正确通过编译。
|
||||
|
||||
//Rust会自动的尝试调用Defref来获得引用,看是否可以满足函数的参数要求
|
||||
//这里我们使用的是MyBox类型的引用,但是print_str需要的是字符串切片,
|
||||
//但是通过Deref 我们可以将&Mybox变为&String,而String也实现了Defref,所以又可以变为&str,这时候就满了函数参数的要求
|
||||
print_str(&MyBox::new(String::from("this is a str")));
|
||||
//这种隐式的转化可以使得我们代码的可读性更好
|
||||
//如果没有的话只能这样子
|
||||
let temp = MyBox::new(String::from("this is a str"));
|
||||
//先通过*temp进行deref,获得String,在通过[...]获得字符串切片
|
||||
print_str(&(*temp)[..]);
|
||||
|
||||
//创建一个可变引用并修改其值,这需要MyBox添加DerefMut trait的支持
|
||||
let mut vnum = MyBox::new(4);
|
||||
*vnum += 1;
|
||||
//drop(vnum); // 主动调用析构函数
|
||||
println!("num = {}", *vnum);
|
||||
}
|
||||
|
||||
fn print_str(s: &str) {
|
||||
println!("{}", s);
|
||||
}
|
||||
|
||||
// 引用的转化会需要满足:当实现的 T的deref trait时 target 是U时,
|
||||
// 1. 可以实现T的可变引用转化为U的可变或者不可变引用
|
||||
// 2. 可以实现T的不可变引用转化为U的不可变引用
|
||||
|
||||
impl<T> DerefMut for MyBox<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for MyBox<T> {
|
||||
fn drop(&mut self) {
|
||||
println!("release a MayBox obj");
|
||||
}
|
||||
}
|
80
p15.smart_point/boxt/src/SmartPoint/rc_cell_t.rs
Executable file
80
p15.smart_point/boxt/src/SmartPoint/rc_cell_t.rs
Executable file
|
@ -0,0 +1,80 @@
|
|||
/// RefCell<T>
|
||||
/// 相较于Rc其和Box一样只能有一个拥有者,但是其可以修改内部的值,Rc只能读.
|
||||
pub trait Messenger {
|
||||
fn send(&self, msg: &str);
|
||||
}
|
||||
use std::cell::RefCell;
|
||||
pub struct LimitTracker<'a, T: 'a + Messenger> {
|
||||
messenger: &'a T,
|
||||
value: usize,
|
||||
max: usize,
|
||||
}
|
||||
|
||||
/// 为所有实现了messenger trait的类型提供一个限制检测的功能,初始化时指定最大值
|
||||
impl<'a, T> LimitTracker<'a, T>
|
||||
where
|
||||
T: Messenger,
|
||||
{
|
||||
pub fn new(messenger: &T, max: usize) -> LimitTracker<T> {
|
||||
LimitTracker {
|
||||
messenger,
|
||||
value: 0,
|
||||
max,
|
||||
}
|
||||
}
|
||||
/// 这是要测试的方法,该方法,根据初始化时定义的max值和当前值的的比值,
|
||||
/// 当超过指定的比利时候发生不同的消息,没有返回值
|
||||
/// 其可以被所有实现了Messenger trait的类型使用。
|
||||
pub fn set_value(&mut self, value: usize) {
|
||||
self.value = value;
|
||||
let percentage_of_max = self.value as f64 / self.max as f64;
|
||||
|
||||
if percentage_of_max >= 1.0 {
|
||||
self.messenger.send("超过限额")
|
||||
} else if percentage_of_max >= 0.9 {
|
||||
self.messenger.send("已使用90%")
|
||||
} else if percentage_of_max >= 0.75 {
|
||||
self.messenger.send("已使用75%")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// 为了测试上面的LimitTracker的set_value方法这里制作一个模拟对象进行测试,
|
||||
/// 因为set_value本身不返回任何返回值就没法使用assert_eq等进行检测,所以
|
||||
/// 这里设计一个模拟对象,并实现send方法,在set_value函数时将发送的字符串存储起来
|
||||
/// 这样跑完set_value之后我们可以通过模拟对象里面的数据来判断是否符合预期
|
||||
/// 从而来获知set_value是否能够正常工作
|
||||
/// 但是Messenger trait的send方法的参数是不可变引用,那么在send函数里面是不可以修改成员的变量的
|
||||
/// 这时候就可以使用RefCell来获得不可变引用的可变
|
||||
struct MockMessenger {
|
||||
sent_messenger: RefCell<Vec<String>>,
|
||||
}
|
||||
|
||||
impl MockMessenger {
|
||||
fn new() -> MockMessenger {
|
||||
MockMessenger {
|
||||
sent_messenger: RefCell::new(vec![]), // 使用RefCell存储数据
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Messenger for MockMessenger {
|
||||
fn send(&self, msg: &str) {
|
||||
// borrow_mut 获得数据的可变引用
|
||||
self.sent_messenger.borrow_mut().push(String::from(msg));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_sends_an_over_75_percent_wrning_messager() {
|
||||
let obj = MockMessenger::new();
|
||||
let mut limit_tracker = LimitTracker::new(&obj, 100);
|
||||
|
||||
limit_tracker.set_value(80);
|
||||
// borrow_mut 获得数据的不可变引用
|
||||
assert_eq!(obj.sent_messenger.borrow().len(), 1);
|
||||
}
|
||||
}
|
28
p15.smart_point/boxt/src/SmartPoint/rc_t.rs
Executable file
28
p15.smart_point/boxt/src/SmartPoint/rc_t.rs
Executable file
|
@ -0,0 +1,28 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
/// # RC<T>
|
||||
/// RC<T>: Reference counting, 是一个带有引用计数的智能指针,其相较于Box来说就是可以同时被多个所有者拥有
|
||||
/// 只有当所有的所有者的都生命周期结束其才会被释放。
|
||||
/// RC提供的不可变引用。
|
||||
|
||||
pub fn learn() {
|
||||
enum List {
|
||||
Cons(i32, Rc<List>),
|
||||
Nil,
|
||||
}
|
||||
// 创建一个RC list。这里为了其能够被b和c
|
||||
let mut a = Rc::new(List::Cons(2, Rc::new(List::Cons(4, Rc::new(List::Nil)))));
|
||||
// 通过Rc::clone将数据的智能指针copy一份,内容还是一样的
|
||||
let b = List::Cons(3, Rc::clone(&a));
|
||||
println!(
|
||||
"strong ref of a is {}, weak is {}",
|
||||
Rc::strong_count(&a),
|
||||
Rc::weak_count(&a)
|
||||
);
|
||||
let c = List::Cons(3, Rc::clone(&a));
|
||||
println!(
|
||||
"strong ref of a is {}, weak is {}",
|
||||
Rc::strong_count(&a),
|
||||
Rc::weak_count(&a)
|
||||
);
|
||||
}
|
9
p15.smart_point/boxt/src/lib.rs
Executable file
9
p15.smart_point/boxt/src/lib.rs
Executable file
|
@ -0,0 +1,9 @@
|
|||
/* 智能指针,是一个概念,其智能本质上是来自于计数的检查,只是交给了编译器去处理销毁的时机,
|
||||
所以对于使用者来说就是无需理会是否由于释放不当导致泄漏或者double free的问题。
|
||||
*/
|
||||
pub mod SmartPoint {
|
||||
pub mod BoxT;
|
||||
pub mod my_box;
|
||||
pub mod rc_cell_t;
|
||||
pub mod rc_t;
|
||||
}
|
47
p15.smart_point/boxt/src/main.rs
Executable file
47
p15.smart_point/boxt/src/main.rs
Executable file
|
@ -0,0 +1,47 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
smart_point_learn();
|
||||
}
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
use boxt::SmartPoint::my_box;
|
||||
use boxt::SmartPoint::rc_t;
|
||||
use boxt::SmartPoint::BoxT;
|
||||
|
||||
fn smart_point_learn() {
|
||||
BoxT::learn();
|
||||
my_box::learn();
|
||||
rc_t::learn();
|
||||
|
||||
#[derive(Debug)]
|
||||
struct StNode {
|
||||
data: String,
|
||||
//使用weak可以使得指向parent并非强引用不会影响parent的释放
|
||||
parent: RefCell<Weak<StNode>>,
|
||||
//使用rc可以使得强引用子节点,只有父节点不再持有时才可释放
|
||||
child: RefCell<Vec<Rc<StNode>>>,
|
||||
}
|
||||
let leaf = Rc::new(StNode{
|
||||
data: String::from("leaf"),
|
||||
parent: RefCell::new(Weak::new()),
|
||||
child: RefCell::new(vec![]),
|
||||
});
|
||||
println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
|
||||
let root = Rc::new(StNode{
|
||||
data: String::from("root"),
|
||||
parent: RefCell::new(Weak::new()),
|
||||
child:RefCell::new(vec![leaf.clone()]),
|
||||
});
|
||||
*(leaf.parent.borrow_mut()) = Rc::downgrade(&root);
|
||||
println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
|
||||
|
||||
println!("root strong ref {}, weak ref {}", Rc::strong_count(&root), Rc::weak_count(&root));
|
||||
println!("leaf strong ref {}, weak ref {}", Rc::strong_count(&leaf), Rc::weak_count(&leaf));
|
||||
|
||||
drop(root);
|
||||
println!("leaf strong ref {}, weak ref {}", Rc::strong_count(&leaf), Rc::weak_count(&leaf));
|
||||
println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
|
||||
// println!("tree is {}", root);
|
||||
}
|
1
p16.fearless_conscurrency/mutex/.gitignore
vendored
Executable file
1
p16.fearless_conscurrency/mutex/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
p16.fearless_conscurrency/mutex/Cargo.toml
Executable file
8
p16.fearless_conscurrency/mutex/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "mutex"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
33
p16.fearless_conscurrency/mutex/src/main.rs
Executable file
33
p16.fearless_conscurrency/mutex/src/main.rs
Executable file
|
@ -0,0 +1,33 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
mutex_learn();
|
||||
}
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
fn mutex_learn() {
|
||||
let strings = Arc::new(Mutex::new(vec![]));
|
||||
let mut ths = vec![];
|
||||
for i in 1..10 {
|
||||
let strings = Arc::clone(&strings); //获得一个强引用
|
||||
let handle = thread::spawn(move || {
|
||||
for _i in 1..3 {
|
||||
strings
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(String::from(format!("thread{}: ", i)) + &(i + _i).to_string());
|
||||
thread::sleep(Duration::from_micros(
|
||||
(((1 as f64) / (i as f64)) * 100.0) as u64, //线程越前的睡的越久
|
||||
));
|
||||
}
|
||||
});
|
||||
ths.push(handle);
|
||||
}
|
||||
|
||||
for th in ths {
|
||||
th.join().unwrap();
|
||||
}
|
||||
println!("strings is {:?}", strings.lock().unwrap());
|
||||
}
|
1
p16.fearless_conscurrency/shared_trait/.gitignore
vendored
Executable file
1
p16.fearless_conscurrency/shared_trait/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
p16.fearless_conscurrency/shared_trait/Cargo.toml
Executable file
8
p16.fearless_conscurrency/shared_trait/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "shared_trait"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
51
p16.fearless_conscurrency/shared_trait/src/lib.rs
Executable file
51
p16.fearless_conscurrency/shared_trait/src/lib.rs
Executable file
|
@ -0,0 +1,51 @@
|
|||
//定义一个Draw trait,所有实现了Draw trait的类型都要具有draw方法。
|
||||
pub trait Draw {
|
||||
fn draw(&self);
|
||||
}
|
||||
|
||||
pub struct Screen {
|
||||
pub components: Vec<Box<dyn Draw>>,
|
||||
}
|
||||
|
||||
impl Screen {
|
||||
pub fn new() -> Screen {
|
||||
Screen { components: vec![] }
|
||||
}
|
||||
pub fn add(&mut self, comp: Box<dyn Draw>) {
|
||||
self.components.push(comp)
|
||||
}
|
||||
pub fn run(&self) {
|
||||
if self.components.len() == 0 {
|
||||
println!("has no components!");
|
||||
return;
|
||||
}
|
||||
for obj in self.components.iter() {
|
||||
obj.draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Button {
|
||||
width: u32,
|
||||
height: u32,
|
||||
text: String,
|
||||
}
|
||||
|
||||
impl Button {
|
||||
pub fn new(width: u32, height: u32, text: &str) -> Button {
|
||||
Button {
|
||||
width,
|
||||
height,
|
||||
text: String::from(text),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Draw for Button {
|
||||
fn draw(&self) {
|
||||
println!(
|
||||
"This is a Button, draw by {}x{} : {}",
|
||||
self.width, self.height, self.text
|
||||
);
|
||||
}
|
||||
}
|
32
p16.fearless_conscurrency/shared_trait/src/main.rs
Executable file
32
p16.fearless_conscurrency/shared_trait/src/main.rs
Executable file
|
@ -0,0 +1,32 @@
|
|||
use shared_trait::*;
|
||||
|
||||
struct SelectBox<'a> {
|
||||
width: i32,
|
||||
height: u32,
|
||||
option: Vec<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> Draw for SelectBox<'a> {
|
||||
fn draw(&self) {
|
||||
println!(
|
||||
"This is a Button, draw by {}x{} : {:?}",
|
||||
self.width, self.height, self.option
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
let mut screen = Screen::new();
|
||||
|
||||
let button = Button::new(30, 40, "Button text");
|
||||
screen.add(Box::new(button));
|
||||
screen.add(Box::new(SelectBox {
|
||||
width: 100,
|
||||
height: 300,
|
||||
option: vec!["option1", "option2", "option3"],
|
||||
}));
|
||||
|
||||
// screen.add(Box::new(String::from("asdad"))); //the trait bound `String: shared_trait::Draw` is not satisfied
|
||||
screen.run();
|
||||
}
|
1
p16.fearless_conscurrency/thread/.gitignore
vendored
Executable file
1
p16.fearless_conscurrency/thread/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
p16.fearless_conscurrency/thread/Cargo.toml
Executable file
8
p16.fearless_conscurrency/thread/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "thread"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
98
p16.fearless_conscurrency/thread/src/main.rs
Executable file
98
p16.fearless_conscurrency/thread/src/main.rs
Executable file
|
@ -0,0 +1,98 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
thread_learn();
|
||||
thread_learn1();
|
||||
thread_learn2();
|
||||
}
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
fn thread_learn() {
|
||||
let handle = thread::spawn(|| {
|
||||
for i in 1..5 {
|
||||
println!("thread {}", i);
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
});
|
||||
for i in 1..5 {
|
||||
println!("main {}", i);
|
||||
thread::sleep(Duration::from_millis(50));
|
||||
}
|
||||
handle.join().unwrap();
|
||||
}
|
||||
|
||||
fn thread_learn1() {
|
||||
let v = vec![1, 2, 3, 4];
|
||||
//闭包是可以直接捕获上下文中的变量并使用的,但是在线程里不行。因为rust无法获知线程合适结束,也就
|
||||
//无法保证v的有效性。如果需要在threa里使用主线程中的变量。需要使用move将其移动到子线程,
|
||||
//这样主线程也就不在拥有v的所有权了
|
||||
|
||||
/* 这种方式会编译不过。因为v不可以再子线程使用 */
|
||||
// let handle = thread::spawn(|| {
|
||||
// println!("thread {:?}", v);
|
||||
// });
|
||||
// println!("main {:?}", v);
|
||||
|
||||
/* 这种方式会编译不过。因为v被移动到子线程主线程已不可使用 */
|
||||
let handle = thread::spawn(move || {
|
||||
println!("thread {:?}", v);
|
||||
});
|
||||
//println!("main {:?}", v);
|
||||
handle.join().unwrap();
|
||||
}
|
||||
|
||||
use std::sync::mpsc;
|
||||
fn thread_learn2() {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let tx1 = mpsc::Sender::clone(&tx); //再产生一个生产者
|
||||
let handle = thread::spawn(move || {
|
||||
let val = String::from("hello");
|
||||
tx.send(val).unwrap();
|
||||
let msgs = vec!["aaaa", "qwer", "rty", "byebye", "zaas"];
|
||||
for msg in msgs {
|
||||
match tx.send(String::from(msg)) {
|
||||
Err(err) => {
|
||||
println!("发送失败 {:?}", err);
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
thread::sleep(Duration::from_micros(20));
|
||||
}
|
||||
});
|
||||
let thandle1 = thread::spawn(move || {
|
||||
let val = String::from("hello");
|
||||
tx1.send(val).unwrap();
|
||||
let msgs = vec!["aaaa1", "qwer1", "rty1", "byebye1", "zaas1"];
|
||||
for msg in msgs {
|
||||
match tx1.send(String::from(msg)) {
|
||||
Err(err) => {
|
||||
println!("发送失败 {:?}", err);
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
thread::sleep(Duration::from_micros(5));
|
||||
}
|
||||
});
|
||||
|
||||
let handle1 = thread::spawn(move || loop {
|
||||
let val = match rx.try_recv() {
|
||||
Ok(msg) => msg,
|
||||
Err(_) => {
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
// let val = rx.recv().unwrap();
|
||||
if val == "byebye" {
|
||||
drop(rx);
|
||||
break;
|
||||
}
|
||||
println!("receive a msg: {}", val);
|
||||
});
|
||||
|
||||
handle.join().unwrap();
|
||||
thandle1.join().unwrap();
|
||||
handle1.join().unwrap();
|
||||
}
|
1
p18.match/match_learn/.gitignore
vendored
Executable file
1
p18.match/match_learn/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
p18.match/match_learn/Cargo.toml
Executable file
8
p18.match/match_learn/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "match_learn"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
107
p18.match/match_learn/src/main.rs
Executable file
107
p18.match/match_learn/src/main.rs
Executable file
|
@ -0,0 +1,107 @@
|
|||
use std::{env::var, sync::atomic::AtomicI32, vec};
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
learn();
|
||||
}
|
||||
|
||||
struct Point {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
fn learn() {
|
||||
let var = 3;
|
||||
match var {
|
||||
//@绑定val,使用..匹配一些值
|
||||
val @ 1..=4 => println!("match 1..4, {}", val),
|
||||
_ => println!("match"),
|
||||
}
|
||||
|
||||
let mut var = vec![1, 2, 3, 4, 5, 6, 7];
|
||||
while let Some(var) = var.pop() {
|
||||
match var {
|
||||
//@绑定val,使用|来进行多重匹配
|
||||
val @ (1 | 3 | 5) => println!("match 1,3,5 => {}", val),
|
||||
_ => println!("miss match val {}", var),
|
||||
}
|
||||
}
|
||||
|
||||
let mut var = vec![1, 2, 3, 4, 5, 6, 7];
|
||||
let cond = 2;
|
||||
while let Some(var) = var.pop() {
|
||||
match var {
|
||||
//使用if语句来进行进一步的限制,b不满足条件的同样会走到其他分支
|
||||
val @ (1 | 3 | 5) if val > cond => println!("match 1,3,5 > {} => {}", cond, val),
|
||||
val @ (1 | 3 | 5) => println!("match 1,3,5 <= {} => {}", cond, val),
|
||||
_ => println!("aa miss match val {}", var),
|
||||
}
|
||||
}
|
||||
|
||||
let var = vec!["first", "second", "third", "fourth", "last"];
|
||||
match var[..] {
|
||||
[last, .., end] => println!("start str is {}, end str is {}", last, end),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let var = ("first", "second", "third", "fourth", "last");
|
||||
// let var = ("first", "second", "third", "fourth", "end");
|
||||
match var {
|
||||
(last, .., "last") => println!("a str end by \"last\", start str is {}", last),
|
||||
_ => println!("not match a tuple end with \"last\""),
|
||||
}
|
||||
|
||||
//let var = (12, "string");
|
||||
let var = (123, "string");
|
||||
match var {
|
||||
(x, y) if x == 12 => {
|
||||
println!("fist is 12, second is {}", y);
|
||||
}
|
||||
(x, y) => {
|
||||
println!("fist is not 12 but {}, second is {}", x, y);
|
||||
}
|
||||
}
|
||||
//解构元组
|
||||
let var = (123, "string");
|
||||
let (x, y) = var;
|
||||
println!("after x:{}, y{}", x, y);
|
||||
|
||||
//解构结构体
|
||||
let var = Point { x: 23, y: 34 };
|
||||
let Point { x, y } = var; //解构时成员明明必须相同
|
||||
println!("after x:{}, y{}", x, y);
|
||||
|
||||
//区间匹配
|
||||
let c = 'c';
|
||||
match c {
|
||||
'a'..='z' => {
|
||||
println!("is a lower char");
|
||||
}
|
||||
'A'..='Z' => {
|
||||
println!("is a upper char");
|
||||
}
|
||||
'0'..='9' => {
|
||||
println!("is a number");
|
||||
}
|
||||
_ => {
|
||||
println!("other char");
|
||||
}
|
||||
}
|
||||
let var = Point { x: 23, y: 34 };
|
||||
deconstruct((23, 34), ((45, 23423), var));
|
||||
|
||||
//可以通过元组将结构体和元组嵌套进行解构
|
||||
//使用_开头的变量即使未使用也不会导致编译器警告。
|
||||
let var = Point { x: 23, y: 34 };
|
||||
let ((_w, _z), Point { x, y }) = ((12, 34), var);
|
||||
|
||||
//_不会绑定变量转移所有权,所以再使用_后所有权还是在原变量上
|
||||
let var = Some(String::from("strzzzz"));
|
||||
if let Some(_) = var {
|
||||
println!("find a string");
|
||||
}
|
||||
println!("str is {:?}", var);
|
||||
}
|
||||
|
||||
fn deconstruct((h, t): (i32, i32), ((w, r), Point { x, y }): ((i32, i32), Point)) {
|
||||
println!("h:{},t:{}, w:{},r:{},x:{},y:{},", h, t, w, r, x, y);
|
||||
}
|
1
panic_except/.gitignore
vendored
Executable file
1
panic_except/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
panic_except/Cargo.toml
Executable file
8
panic_except/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "panic_except"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
0
panic_except/hello_word.txt
Executable file
0
panic_except/hello_word.txt
Executable file
1
panic_except/name.txt
Executable file
1
panic_except/name.txt
Executable file
|
@ -0,0 +1 @@
|
|||
Ekko.bao
|
181
panic_except/src/main.rs
Executable file
181
panic_except/src/main.rs
Executable file
|
@ -0,0 +1,181 @@
|
|||
use std::{fs::File, io::{self, ErrorKind, Read}, net::IpAddr};
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
|
||||
/* panic 属于不可恢复的错误 */
|
||||
//主动触发一个panic
|
||||
// panic!("crash and burn!")
|
||||
|
||||
//在panic的时候控制台会输出很多信息,我们可以根据这些信息来获知崩溃的位置。以及调用链
|
||||
//我们可以设置一些环境变量来决定信息的多少:
|
||||
// set RUST_BACKTRACE=0 关闭触发异常时的堆栈回溯。仅显示崩溃信息
|
||||
// set RUST_BACKTRACE=1 打开堆栈回溯
|
||||
// set RUST_BACKTRACE=full 显示特别详细的堆栈回溯信息
|
||||
|
||||
|
||||
//访问其他代码触发异常 比如这里访问越界。会在vec的代码里面发生panic
|
||||
/* thread 'main' panicked at src\main.rs:9:23:
|
||||
index out of bounds: the len is 3 but the index is 99
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
*/
|
||||
// let var = vec![1,2,3];
|
||||
// println!("{}", var[99]);
|
||||
|
||||
/* 在代码编写中可能会有一些异常的参数,值或者状态出现在代码运行时,这时候我们应该处理这些值避免进一步蔓延甚至导致panic的发生
|
||||
处理的方式可能是纠错,自行恢复也有可能是返回错误到更上层让其处理;
|
||||
也有可能是调用别人提供的功能,别人返回了错误,我们需要处理这些错误。在其他的语言中通常要么是通过一个返回值来判断,
|
||||
亦或是捕获异常并进行异常的处理,但是在RUST中是没有异常的捕获处理方式的。
|
||||
其通过Result枚举来进行错误的承载和处理,
|
||||
在我们的coding过程中应该按照:
|
||||
1. 如果这个函数必定不会失败。那么返回值就是根据需要的返回值类型
|
||||
2. 如果这个函数不一定会成功,那么返回值就是Result枚举,让上层必须处理可能存在的错误
|
||||
3. 如果函数内部可能会发生错误,但是错误一旦出现不可恢复那么应该使用panic进行终止程序
|
||||
*/
|
||||
|
||||
//尝试打开一个文件,因为文件不一定会能够打开失败,所以这里返回值Result类型我们必须要进行处理
|
||||
let f = File::open("hello_word.txt");
|
||||
//这里使用match 表达式进行处理, 保证f一定是可用的
|
||||
let mut f = match f{
|
||||
Ok(file) => file,// 成功-> 将文件对象作为match表达式的值进行返回
|
||||
Err(error) => {
|
||||
//这里根据错误的类型进行处理。
|
||||
match error.kind() {
|
||||
//如果文件不存在则尝试创建一个空文件, 并返回
|
||||
ErrorKind::NotFound => match File::create("hello_word.txt") {
|
||||
Ok(file) => file,
|
||||
Err(error) => panic!("{}", error),//创建失败错误直接panic
|
||||
}
|
||||
//其他类型错误直接panic
|
||||
_ => panic!("{}", error),
|
||||
}
|
||||
}
|
||||
};
|
||||
/*
|
||||
从上面可以看出对于可能会失败的接口采用Result的返回值可以使得,
|
||||
调用者必须处理潜在的错误来保证结果是可用的,以及对于错误会强制你进行选择,该如何处理,
|
||||
比如上面的例子:
|
||||
对于open失败的情况根据错误的类型进行差异化的处理。直到所有的错误得到正视并处理,整个open file的区块才算结束
|
||||
|
||||
这种方式可以使得代码的错误处理分支极其完备,减少大量我们认为不会出现的异常错误,而在发生时按照非预期的流程蔓延开来。
|
||||
当然上面match不断的嵌套是一件很痛苦的处理过程!!!
|
||||
*/
|
||||
//如果代码能走到这里说明f一定是可用的状态
|
||||
let mut buff = String::new();
|
||||
let _ = f.read_to_string(&mut buff);
|
||||
|
||||
println!("file conext is [{}]", buff);
|
||||
|
||||
//对比上面的方式,下面这个函数的处理方式就是如果失败了返回到调用者使其进行
|
||||
//决断,确定错误的处理方式,避免在函数内进行panic的情况。
|
||||
match read_username_from_file() {
|
||||
Ok(name) => println!("name is {}", name),
|
||||
Err(error) => println!("err happend! {}", error),
|
||||
}
|
||||
|
||||
/* 但是即使是将错误进行传播,也需要大量的match 表达式进行匹配并处理,
|
||||
并且随着函数内部功能的增加,需要处理的失败的代码大大增多,
|
||||
所以RUST提供了一个专门的运算符 ? 来减少这部分match代码的代码量,
|
||||
使得可以更多的专注在正常的代码逻辑上。
|
||||
*/
|
||||
match read_username_from_file1() {
|
||||
Ok(name) => println!("name is {}", name),
|
||||
Err(error) => println!("err happend! {}", error),
|
||||
}
|
||||
|
||||
//使用链式调用减少代码,可见如下函数的实现
|
||||
match read_username_from_file2() {
|
||||
Ok(name) => println!("name is {}", name),
|
||||
Err(error) => println!("err happend! {}", error),
|
||||
}
|
||||
|
||||
// 其他的Result处理方式
|
||||
other_panic();
|
||||
}
|
||||
|
||||
//定义一个函数返回值类型是Result
|
||||
fn read_username_from_file() -> Result<String, io::Error> {
|
||||
/* 这里演示了一种传播错误的方法,我们通过将函数的返回值定义为Result的方式
|
||||
当我们在函数的内部实现遇到了一些问题时我们可以将问题传播着了解问题的信息,
|
||||
并交给他去决定该怎么处理,
|
||||
*/
|
||||
let f = File::open("name.txt");
|
||||
let mut f = match f {
|
||||
Ok(file) => file,
|
||||
//如果打开失败了那么就将失败的错误传播回去
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let mut buff = String::new();
|
||||
match f.read_to_string(&mut buff) {
|
||||
//如果读取失败了则也是返回一个错误
|
||||
Err(error) => return Err(error),
|
||||
Ok(size) => {
|
||||
//如果size为0主动构造一个错误进行返回
|
||||
if size == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "File is empty"));
|
||||
}
|
||||
},
|
||||
}
|
||||
Ok(buff)
|
||||
}
|
||||
|
||||
//这是将read_username_from_file使用 ? 运算符进行简化之后的实现
|
||||
fn read_username_from_file1() -> Result<String, io::Error> {
|
||||
/* ? 表达式可以使得,当错误发生时将错误进行Return
|
||||
通过这种方式可以大大减少代码量,当然这种语法必须建立在错误的类型都是一致的情况下
|
||||
如果错误类型不一致或者源错误不能转化为目标错误的话是不能这样使用的,
|
||||
另外?运算符号只能在Result或者option为返回值的函数才能够使用,否则会编译报错,
|
||||
*/
|
||||
let mut buff = String::new();
|
||||
let mut f = File::open("name.txt")?;// ? 运算符
|
||||
let size = f.read_to_string(&mut buff)?;// ? 运算符
|
||||
if size == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "File is empty"));
|
||||
}
|
||||
Ok(buff)
|
||||
}
|
||||
|
||||
//使用链式调用进一步简化代码
|
||||
fn read_username_from_file2() -> Result<String, io::Error> {
|
||||
/* 因为使用了?运算符可以保证如果函数可以走到后续的代码说明一定是成功了的
|
||||
我们就可以肆无忌惮的使用这个结果,
|
||||
所以可以使用链式调用来进一步缩减中间值的使用而保证一定是安全的
|
||||
*/
|
||||
let mut buff = String::new();
|
||||
if File::open("name.txt")?.read_to_string(&mut buff)? == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "File is empty"))
|
||||
}
|
||||
Ok(buff)
|
||||
}
|
||||
|
||||
|
||||
fn other_panic() {
|
||||
/* 除了主动的panic宏来进行错误检测后的主动panic。我们还有另外的panic的方式: unwrap 和 except
|
||||
unwrap可以在结果为Err或者为None的时候发生panic,但是他不提供任何自定义的信息
|
||||
except可以附加一些信息。
|
||||
那么为什么会需要这两个东西呢,而不是通过match表达式检测异常?
|
||||
1. 因为开发过程中在初期阶段,我们可能没办法完整处理所有的错误,因为有些组件还没准备完成
|
||||
2. 亦或是这是一个简单程序。不需要那么健壮性,也不需要考虑各种异常。
|
||||
3. 这就是一个测试程序,目标测试的接口函数就应该不发生任何异常。
|
||||
当我们在使用一些返回值是Result的函数接口的时候就可以使用这两个方法进行结果的检测和处理
|
||||
*/
|
||||
|
||||
//我们如果知道这一定是不可能会失败的那么我们可以用unwrap来进行结果的提取,而不必理会
|
||||
//比如这里我们明确他是一个常量的合法的IP地址,那么就直接使用unwrap来提取即可
|
||||
let ip:IpAddr = "127.0.0.1".parse().unwrap();
|
||||
//他和下面这样是一样的效果,但是我们知道不可能失败错误就没必要这么复杂了
|
||||
// let ip:IpAddr = match "127.0.0.1".parse() {
|
||||
// Ok(ipaddr) => ipaddr,
|
||||
// Err(error) => panic!(),
|
||||
// };
|
||||
|
||||
//这是使用except的方式,可以添加一些信息
|
||||
let ip:IpAddr = "127.0.0.1".parse().expect("解析ip地址失败了");
|
||||
}
|
||||
|
||||
|
||||
/* 可以看出Rust在对于错误的处理的安全上也是及其苛刻的,
|
||||
他不仅要求你知晓你所使用的接口函数可能会存在的错误。
|
||||
而且也通过语法尽可能的鼓励你使用Result来将错误不隐藏,及时传播或者处理!
|
||||
同时为了避免程序陷入为了处理错误而带来的大量异常处理代码,加上了?运算符大大减少了代码量。
|
||||
*/
|
1
string_learn/.gitignore
vendored
Executable file
1
string_learn/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
string_learn/Cargo.toml
Executable file
8
string_learn/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "string_learn"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
58
string_learn/src/main.rs
Executable file
58
string_learn/src/main.rs
Executable file
|
@ -0,0 +1,58 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
string_learn();
|
||||
}
|
||||
|
||||
fn string_learn() {
|
||||
//字符串字面值 类型:&str
|
||||
let str1 = "this is a &str";
|
||||
|
||||
//从字符串字面值创建一个string,类型:String
|
||||
let str2 = String::from("hello");
|
||||
|
||||
//字符串引用。 类型: &string
|
||||
let str3 = &str2;
|
||||
|
||||
//字符串切片 类型:&str
|
||||
let str3_1 = &str2[0..2];
|
||||
|
||||
//拼接字符串
|
||||
let str4 = String::from("world");
|
||||
|
||||
//let str5 = str2 + str4;//这是不允许的,+ 右边必须是字符串的切片或者字符串字面值
|
||||
let str5 = str2 + &str4; //经过这句之后 str2 的所有权已经移交到 + 的实现函数里面了,使用后就被释放了。
|
||||
|
||||
//使用format 宏去连接字符串
|
||||
let str6 = format!("{}{}", str5, "aaa");
|
||||
|
||||
//字符串不允许使用下标进行访问:
|
||||
//rust的字符串是unicode编码的,每个“字母”所占用的byte数是不等长得。
|
||||
println!("长度为 {}", "中文".len());//这里的结果应该是6,每个中文占三个字节
|
||||
println!("长度为 {}", "AB".len());//这里的结果应该是2,每个ascii占1bytes
|
||||
/* 除此之外,还有很多其他的国家、文化的语言,可能是多个字母才能表达一个意思的,还有的是逆序的
|
||||
所以也没法进行拆分单个 “字母”,这里的字母指的就是unicode标量值
|
||||
*/
|
||||
//获取字符串对应的内存数据
|
||||
let buff = "中文".bytes();
|
||||
print!("中文 bytes is [");
|
||||
for it in buff {
|
||||
print!("{} ", it);
|
||||
}
|
||||
println!("]");
|
||||
|
||||
//当我们使用字符串切片的获取一部分内容的时候需要额外小心,
|
||||
//每种语言其char的大小是不一样的,所以如果获取的内容处于两个char之间就会panic
|
||||
let s = &"中文"[0..3];
|
||||
println!("字符串切片 中文[0..3] is {}", s);
|
||||
|
||||
//let s = &"中文"[0..2];//一个中文char占用3byte,这里只取了两个byte就会panic
|
||||
|
||||
//遍历所有char可以用chars方法
|
||||
print!("遍历所有char: [");
|
||||
for it in "这是一个中文+表情💚DE字符串".chars() {
|
||||
print!("{} ", it);
|
||||
}
|
||||
println!("]");
|
||||
|
||||
//println!("char0 is {}", "adb"[0]); //默认不支持根据索引进行获取,因为无法保证能够获取到一个有效的unicode标量值 char
|
||||
}
|
1
struct_learn/.gitignore
vendored
Executable file
1
struct_learn/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
struct_learn/Cargo.toml
Executable file
8
struct_learn/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "struct_learn"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
170
struct_learn/src/main.rs
Executable file
170
struct_learn/src/main.rs
Executable file
|
@ -0,0 +1,170 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
struct01();
|
||||
let user = build_user(String::from("qwer"), String::from("ertyfd"));
|
||||
println!("user info name:{}, age:{}, email:{}", user.name, user.age, user.email);
|
||||
let user1 = ref_build_use1(user);
|
||||
println!("user1 info name:{}, age:{}, email:{}", user1.name, user1.age, user1.email);
|
||||
|
||||
sturct_empty();
|
||||
|
||||
tuple_struct();
|
||||
struct_method_test();
|
||||
}
|
||||
|
||||
|
||||
fn struct01()
|
||||
{
|
||||
//定义一个结构体类型,使用struct为关键字,后面跟其名字,
|
||||
//定义成员,类型后置,每个成员定义后都需要使用逗号分隔
|
||||
struct User{
|
||||
name:String, //类型后置,逗号分隔
|
||||
email:String,
|
||||
age:i32,
|
||||
is_man:bool,
|
||||
sign_cnt:i64, //最后一个成员也需要加逗号
|
||||
} //无需使用分号
|
||||
|
||||
// 使用指定的类型初始化一个实例,每个成员都必须进行初始化赋值。
|
||||
let mut user = User{
|
||||
name:String::from("ekko.bao"),
|
||||
email:String::from("yucang_bao@126.com"),
|
||||
age:8,
|
||||
is_man:true,
|
||||
sign_cnt:2222222,
|
||||
};
|
||||
//访问成员使用点分隔符进行访问
|
||||
println!("user info name:{}, age:{}, email:{}", user.name, user.age, user.email);
|
||||
//struct所有成员mut是整体的不可单个设置,要么所有都可被修改要么都不可被修改。
|
||||
user.age = 888;
|
||||
}
|
||||
|
||||
struct User{
|
||||
name:String, //类型后置,逗号分隔
|
||||
email:String,
|
||||
age:i32,
|
||||
is_man:bool,
|
||||
sign_cnt:i64, //最后一个成员也需要加逗号
|
||||
}
|
||||
|
||||
fn build_user(name:String, email:String) -> User {
|
||||
// 这里展示了一种初始化struct的简便方法:
|
||||
User{
|
||||
name, //等效于 name:name, 当想要初始化的成员名和变量名一致时可直接省略赋值操作
|
||||
email,//等效于 name:email
|
||||
age:10,
|
||||
is_man:false,
|
||||
sign_cnt:66666,
|
||||
}
|
||||
}
|
||||
|
||||
fn ref_build_use1(user1:User) -> User {
|
||||
// 这里展示了一种初始化struct的简便方法:
|
||||
// 当我们一部分内容想要自定义初始化一部分来自另外一个stuct,
|
||||
// 那么我们可以使用如下的语法进行
|
||||
User{
|
||||
/* 填写自定义初始化的内容 */
|
||||
name:String::from("user1.bao"),
|
||||
email:String::from("zzzzzzz@126.com"),
|
||||
age:77,
|
||||
/* 选择继承已有结构体,未初始化的其他成员将会使用该结构体进行初始化
|
||||
该结构体必须是和当前想要初始化的结构体一样的类型才可以
|
||||
*/
|
||||
..user1
|
||||
}
|
||||
}
|
||||
|
||||
fn sturct_empty(){
|
||||
// stuct可以是空成员的,这个是为了只实现一些方法但是不提供成员数据的应用场景。
|
||||
struct empty_struct{
|
||||
|
||||
}
|
||||
|
||||
let test = empty_struct{};
|
||||
}
|
||||
|
||||
fn tuple_struct(){
|
||||
// tuple struct 是类似元组的struct,没有成员名只有类型。
|
||||
// 使用括号进行包裹所有成员
|
||||
struct RGB(u8,u8,u8);
|
||||
let color = RGB(34,233,123);
|
||||
println!("color is R:{} G:{} B:{}", color.0, color.1, color.2);
|
||||
|
||||
struct POINT(u8,u8,u8);
|
||||
let point = POINT(34,233,123);
|
||||
println!("point is X:{} Y:{} Z:{}", point.0, point.1, point.2);
|
||||
|
||||
// let point:POINT = color; // color和point虽然成员的类型和个数都是一样的,但是是不同的类型!,所以不可以进行绑定
|
||||
let color1 = RGB{0:23,..color};//同样可以使用这种继承部分成员的赋值方法
|
||||
println!("color1 is R:{} G:{} B:{}", color1.0, color1.1, color1.2);
|
||||
|
||||
let color2 = color1; //color1 所有权移交给color2 color1不可在使用
|
||||
println!("color2 is R:{} G:{} B:{}", color2.0, color2.1, color2.2);
|
||||
//println!("color1 is R:{} G:{} B:{}", color1.0, color1.1, color1.2);//不可使用
|
||||
}
|
||||
|
||||
|
||||
//注解 ,可以打印结构体内容
|
||||
#[derive(Debug)]
|
||||
struct Rectangle {
|
||||
width: i32,
|
||||
height: i32,
|
||||
}
|
||||
|
||||
//定义结构体Rectangle的实现
|
||||
impl Rectangle{
|
||||
/* 第一个参数为self的是该结构体的方法, 就类似于类的成员函数 */
|
||||
//求长方形面积
|
||||
fn area (&self) -> i32{ // 使用引用的方式使用self, 可以不显式的标注类型
|
||||
//fn area (self: &Rectangle) -> i32{ // 使用类型后置标注的方式也是ok的
|
||||
//fn area (self: & mut Rectangle) -> i32{ // 也可以接上mut关键字, 这使得可以修改成员变量
|
||||
// self.width = self.height;
|
||||
//fn area (self: Rectangle) -> i32{ // 非引用的方式使用也是ok的,但是要注意所有权的问题
|
||||
self.width * self.height
|
||||
}
|
||||
/* 第一个参数不是self的是该结构体的关联函数 */
|
||||
fn new(width: i32, height: i32) -> Rectangle{
|
||||
Rectangle{
|
||||
width,
|
||||
height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//impl可以分布在多个地方。只需要进行impl包裹即可
|
||||
impl Rectangle{
|
||||
//根据传入的矩形创建一个外接的正方形
|
||||
fn create_hold_square(other:&Rectangle) -> Rectangle{
|
||||
let width = if other.width > other.height {other.width} else {other.height};
|
||||
Rectangle{
|
||||
width,
|
||||
height: width,
|
||||
}
|
||||
}
|
||||
|
||||
//检查一个矩形是否可以完全按包住另一个矩形
|
||||
//像这样类型后置的申明self的类型的方式也是可以的
|
||||
fn can_hold(self: &Rectangle, other:&Rectangle ) -> bool{
|
||||
if self.width >= other.width && self.height >= other.height {
|
||||
true
|
||||
}
|
||||
else{
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn struct_method_test(){
|
||||
//使用关联函数初始化一个矩形
|
||||
let rect = Rectangle::new(30,30);
|
||||
println!("rect is{:#?}, area is {}", rect, rect.area());
|
||||
|
||||
let rect = Rectangle{
|
||||
width: 60,
|
||||
height: 40,
|
||||
};
|
||||
|
||||
let rect1 = Rectangle::create_hold_square(&rect);
|
||||
println!("rect is {:?}, rect1 is {:?}", rect, rect1);
|
||||
println!("rect1 {} hold rect", if rect1.can_hold(&rect) {"can"} else {"can not"});
|
||||
}
|
1
trait_learn/.gitignore
vendored
Executable file
1
trait_learn/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
trait_learn/Cargo.toml
Executable file
8
trait_learn/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "trait_learn"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
116
trait_learn/src/lib.rs
Executable file
116
trait_learn/src/lib.rs
Executable file
|
@ -0,0 +1,116 @@
|
|||
/* 我们设计一个summary trait我们希望这个trait可以提供一个摘要的功能,
|
||||
比如我们的博客,微博,文章等都需要一个摘要,方便在信息流展示时进行展示。
|
||||
*/
|
||||
pub trait Summary {
|
||||
//定义一个summary trait下的方法 summerize,该方法返回一个摘要的字符串
|
||||
fn summarize(&self) -> String;
|
||||
/* trait 定义得到每个方法在使用了impl trait的数据结构中都需要完整实现所有的方法 */
|
||||
//fn reply(&self) -> bool; // 比如定义了这个方法后,下面引入了Summary的NewsArticle和Tweet都需要进行实现!
|
||||
|
||||
// 定义一个author方法,并将其实现,这就是trait的默认方法
|
||||
// 如果引入了summary的数据结构没有实现该方法,那么在调用时就会调用该默认方法。
|
||||
fn author(&self) -> String {
|
||||
String::from("unkown")
|
||||
}
|
||||
}
|
||||
|
||||
// 定义一个新闻文章的数据结构,
|
||||
pub struct NewsArticle {
|
||||
pub headline:String, // 标题
|
||||
pub location:String, // 地点
|
||||
pub author: String, // 作者
|
||||
pub content:String, // 内容
|
||||
}
|
||||
//定义一个推文的数据结构
|
||||
pub struct Tweet {
|
||||
pub username:String,//用户名
|
||||
pub content:String, // 内容
|
||||
pub reply: bool, //是否可回复
|
||||
pub retweet:bool, //是否可转发
|
||||
}
|
||||
|
||||
|
||||
//为 NewsArticle 实现 summary
|
||||
// 语法为 impl <trait name> for <data_type>
|
||||
impl Summary for NewsArticle {
|
||||
fn summarize(&self) -> String {
|
||||
//对于新闻来说我们使用标题,作者和地点。生成一个摘要信息
|
||||
format!("{}, by {} ({})",self.headline, self.author(), self.location)
|
||||
}
|
||||
|
||||
//覆盖掉Summary的默认author实现。在NewsArticle 实例调用该方法时只能调用到该函数,
|
||||
//并且该函数无法再调用到默认的author函数
|
||||
fn author(&self) -> String {
|
||||
let mut one = String::new();
|
||||
self.author.clone_into(&mut one);
|
||||
one
|
||||
}
|
||||
}
|
||||
|
||||
impl Summary for Tweet {
|
||||
fn summarize(&self) -> String {
|
||||
//对于推文来说我们使用作者和推文内容作为摘要
|
||||
format!("{}: {}", self.author(), self.content)
|
||||
}
|
||||
}
|
||||
|
||||
/* 经过上面针对两种不同的数据结构对于summery的实现,
|
||||
我们可以使得他们都有一种共同的功能-> 生成摘要
|
||||
*/
|
||||
|
||||
// 通过引入Dsiplay trait来使得本地的数据结构Tweet具备 Display的功能
|
||||
use std::{fmt, fmt::{Display, Formatter}};
|
||||
impl Display for Tweet {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
//实现Display trait的功能
|
||||
write!(f, "{}:\n {} \n reply:{} retweet:{}", self.username, self.content, self.reply, self.retweet)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
我们指定的函数参数时,通常需要指定参数的类型。只有对应类型的数据才可以传入,
|
||||
RUST提供了另外一种限制参数类型的方式:impl trait。
|
||||
通过这种定义形参的方法可以使得只有实现了指定trait的类型才可以使用该函数,但是又不限制具体的数据类型,
|
||||
这种使用方式适合针对某个trait所做的功能,使得所有实现该trait的类型都可以免于实现类似的函数,减少代码。
|
||||
var: impl xxx
|
||||
*/
|
||||
//定义一个通知函数。只有具备summary trait的类型才可以作为参数传递进来。
|
||||
//这样的一个通用的通知函数,使得不用为每个想要通知的数据类型都写一个类似的函数
|
||||
pub fn notify(not: impl Summary){
|
||||
println!("new notification: [{}]", not.summarize());
|
||||
}
|
||||
|
||||
// 传入两个参数都用impl Summary修饰,表示not1和not2都是实现了Summary的类型,但是他们不一定是同一个数据类型。
|
||||
pub fn notify1(not1: impl Summary, not2: impl Summary){
|
||||
println!("new notification1: [{}]", not1.summarize());
|
||||
println!("new notification1: [{}]", not2.summarize());
|
||||
}
|
||||
|
||||
// 传入两个参数都都是类型T,T类型要求是符合Summary trait的类型,
|
||||
// 这里要注意:not1和not2都必须是同一种类型才可以
|
||||
pub fn notify2<T: Summary>(not1: T, not2: T){
|
||||
println!("new notification2: [{}]", not1.summarize());
|
||||
println!("new notification2: [{}]", not2.summarize());
|
||||
}
|
||||
|
||||
//要求参数同时满足多个trait的类型限制
|
||||
pub fn some_func0<T: Summary+Copy>(not1: T){
|
||||
}
|
||||
|
||||
//多个参数,每个参数各自限制不用的trait限制
|
||||
pub fn some_func<T: Summary+Copy, U: Summary + Display>(not1: T, not2: U){
|
||||
}
|
||||
//简化的写法使用Where来将限制提在函数签名外,使得函数名和参数列表比较靠近,where可以换行,多行来写
|
||||
pub fn some_func1<T, U>(not1: T, not2: U) -> i32
|
||||
where T: Summary+Copy, U: Summary + Display
|
||||
{
|
||||
0
|
||||
}
|
||||
|
||||
// 返回值同样可以限制为实现了某个trait的数据类型, 也可以多种trait限制叠加。
|
||||
// 返回一个实现了Summary trait和Copy类型的变量
|
||||
pub fn some_func2<T, U>(not1: T, not2: U) -> impl Summary+Copy
|
||||
where T: Summary+Copy, U: Summary + Display
|
||||
{
|
||||
not1
|
||||
}
|
137
trait_learn/src/main.rs
Executable file
137
trait_learn/src/main.rs
Executable file
|
@ -0,0 +1,137 @@
|
|||
use core::num;
|
||||
|
||||
use trait_learn::{self, NewsArticle, Summary, Tweet};
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
trait_learn();
|
||||
largest_test();
|
||||
displaytest();
|
||||
}
|
||||
|
||||
|
||||
/* trait:RUST中实现一些接口或者特性或者共享功能的机制
|
||||
在C中我们一般对外提供功能都是使用的一个头文件去申明接口,然后在提供一个实现了这个接口的lib,
|
||||
这样外部的代码可根据接口进行调用实现功能,而内部的具体实现以及内部的数据结构就屏蔽在lib里,
|
||||
对于rust来说,对外提供的接口是通过pub关键字进行暴露的,也能够达到上述的目的,那么trait是做什么用呢?
|
||||
|
||||
在我的理解trait是在接口之上的一个东西,也可以反过来理解,他定义了一个接口应当具备的功能。
|
||||
当我们定义某个trait后,所有实现了这个trait的数据结构他们在某种程度上就是有一种共同的功能,
|
||||
那么在实际使用时就可以利用这种共性去编写函数,减少对于不同类型相同功能的使用代码。
|
||||
|
||||
比如我们都熟知的 print, 它就是可以将所有支持了Display这个trait的数据结构都进行打印到终端,
|
||||
我们只需在实现某个数据结构时将这个Display trait实现,其即可作为参数传递给print函数!
|
||||
就这个Display来说。其就类似于C++中某个类重载了<<操作符,或者python中重载了 _str_()一样。
|
||||
|
||||
那么为什么要单独把trait这个概念领出来呢,而不是说像其他语言那样使用重载的概念,这是因为
|
||||
这个trait还可以作为参数的限制的一部分,也就是说,我们通过泛型可以接受各种数据类型,但是可能有些
|
||||
数据类型并不符合我们的要求,因为我们这个函数的具体实现完成的功能,必须要求类型具备某些属性才行,
|
||||
这时候在比如C++中可能会用模板的type list来限制,只有在list里面的类型才可以传入这个函数,
|
||||
类似这样
|
||||
template<T>
|
||||
typename enable_if<xxx>
|
||||
RUST是倒过来进行筛选,直接在泛型的标注上,标注只有具备这个trait的类型才可以被调用!
|
||||
类似这样
|
||||
T: impl xxx
|
||||
这样有什么好处?,我们可以不必为新增加了某个实现xxx的数据类型进行修改该函数的代码,
|
||||
*/
|
||||
fn trait_learn() {
|
||||
// 定义一个 新闻实例
|
||||
let news = NewsArticle {
|
||||
author: String::from("ekko.bao"),
|
||||
content: String::from("Th ShangHai Peenguins once again are the best hockey team in the NHL."),
|
||||
headline: String::from("Penguins win the stanley Cup Championship"),
|
||||
location: String::from("ShangHai, China"),
|
||||
};
|
||||
|
||||
// 定义一个tweet实例
|
||||
let tweet = Tweet {
|
||||
username: String::from("ekko.bao"),
|
||||
content: String::from("of course, as you probably already know, people."),
|
||||
reply: false,
|
||||
retweet: false,
|
||||
};
|
||||
|
||||
println!("1 new tweet: {}", tweet.summarize());
|
||||
println!("New article: {}", news.summarize());
|
||||
|
||||
// 对于实现了 Display的类型可直接进行打印
|
||||
println!("tweet:{}", tweet);
|
||||
//NewsArticle未实现Diaply trait,无法进行打印。
|
||||
//println!("article:{}", news);
|
||||
|
||||
//可以使用 notify ,因为 Tweet 和 NewsArticle 都实现了 Summary
|
||||
//trait_learn::notify(tweet);
|
||||
//trait_learn::notify(news);
|
||||
//无法使用notify:the trait bound `&str: Summary` is not satisfied
|
||||
//trait_learn::notify("new notify");
|
||||
|
||||
// news和tweet 并不是同一种类型.是不可以调用的 notify2的
|
||||
//trait_learn::notify2(news, tweet);
|
||||
|
||||
// news和tweet 都实现了Summary,可以调用notify1
|
||||
trait_learn::notify1(news, tweet);
|
||||
}
|
||||
/*
|
||||
trait 只能在对应的crate 里面才能够进行实现,比如trait是在src/lib.rs里面定义的所以,
|
||||
对应的impl也只能在lib.rs里面进行实现,不能在main.rs里面实现,
|
||||
这样可以防止其他人使用lib的时候恶意破坏lib的代码。
|
||||
如下的代码将会报错:
|
||||
*/
|
||||
// impl Summary for NewsArticle {
|
||||
// }
|
||||
|
||||
|
||||
//使用trait限制,只有实现了比较和拷贝的类型才可以call largest函数
|
||||
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
|
||||
let mut lag = list[0];
|
||||
for &item in list.iter() {
|
||||
if lag < item { // PartialOrd trait
|
||||
lag = item; // Copy trait
|
||||
}
|
||||
}
|
||||
lag
|
||||
}
|
||||
fn largest_test() {
|
||||
let number_list = vec![24, 50, 35, 100 ,65];
|
||||
let result = largest(&number_list);
|
||||
println!("largest is {}", result);
|
||||
}
|
||||
|
||||
|
||||
struct Point {
|
||||
x:i32,
|
||||
y:i32
|
||||
}
|
||||
|
||||
use std::fmt;
|
||||
impl fmt::Display for Point{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "({}, {})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait AddStr {
|
||||
fn add_str(&self) -> String;
|
||||
}
|
||||
|
||||
// 为所有支持dispaly trait类型添加一个对于AddStr的支持
|
||||
// AddStr 这个示例trait的目的是为了加一个前导的字符串
|
||||
impl <T> AddStr for T
|
||||
where T: fmt::Display
|
||||
{
|
||||
fn add_str(&self) -> String{
|
||||
String::from("head str") + &self.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn displaytest(){
|
||||
let var = Point{
|
||||
x:12,
|
||||
y:12,
|
||||
};
|
||||
|
||||
let str = var.to_string();
|
||||
let str1 = var.add_str();
|
||||
println!("{} -> {}", str, str1);
|
||||
}
|
1
variable/.gitignore
vendored
Executable file
1
variable/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
8
variable/Cargo.toml
Executable file
8
variable/Cargo.toml
Executable file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "variable"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
117
variable/src/main.rs
Executable file
117
variable/src/main.rs
Executable file
|
@ -0,0 +1,117 @@
|
|||
|
||||
//学习不可变变量,可变变量,const变量,类型标注
|
||||
|
||||
//const变量,在函数外函数内都可以定义,必须加上类型标注
|
||||
const MAX_POINT:i32 = 90;
|
||||
|
||||
//rust 是静态强类型,编译时语言
|
||||
|
||||
fn main() {
|
||||
const MAX_POINT:u32 = 999;
|
||||
|
||||
//申明一个不可变变量
|
||||
let immute_var:&str = " ";
|
||||
|
||||
//如下使用将会报错,因为给不可变变量二次赋值
|
||||
//immute_var = "qwer";
|
||||
|
||||
//重新使用let关键字可以使得用一个同名的一个不可变变量,之前的变量就被shadow掉了
|
||||
let immute_var = "aasd";
|
||||
|
||||
//同理可以修改变量的类型,因为这其实是一个新变量
|
||||
let immute_var:u32 = 90;
|
||||
|
||||
//定义一个可变变量
|
||||
let mut mute_var = 78;
|
||||
//给可变变量重新绑定一个新的值
|
||||
mute_var = 45;
|
||||
|
||||
//不可以修改变量类型, 静态强类型!
|
||||
//mute_var="asdasd"
|
||||
|
||||
data_type();
|
||||
compound();
|
||||
array()
|
||||
}
|
||||
fn data_type(){
|
||||
//变量类型:
|
||||
//变量类型分为标量类型和复合类型:
|
||||
/*
|
||||
标量:
|
||||
i8 i16 i32 i64 i128
|
||||
u8 u16 u32 u64 u128
|
||||
f32 f64
|
||||
默认的数据类型分别是 i32和f64
|
||||
占用的字节数量和数据类型的bit位有关系
|
||||
|
||||
*/
|
||||
|
||||
//类型标注
|
||||
let num:u16= 666;
|
||||
//数据后后置类型
|
||||
let num= 0x1234u16;
|
||||
//支持多种赋值方式
|
||||
let num= 0x1234;//16进制
|
||||
let num= 1234; //10进制
|
||||
let num= 1_234_234;//下划线分割,增强可阅读性
|
||||
let num= 0b1111_0000;//二进制
|
||||
let num=0o123_675;//8进制
|
||||
|
||||
//使用ASCII的值进行复制,仅限u8类型
|
||||
let num = b'A';//
|
||||
let num:u8 = b'|';//
|
||||
//let num:u16 = b'A';//报错类型不匹配
|
||||
|
||||
//数据溢出,进行环绕,
|
||||
//let num:u8 = 255 + 1; // => num = 0
|
||||
//println!("255+1 = {}", num);
|
||||
|
||||
//强类型:不可以从i32->f32直接赋值!
|
||||
//let num1:f32 = num;
|
||||
|
||||
//定义一个字符变量,Unicode变量,四个bytes。和字符串不同,他是单引号的!
|
||||
let c:char='A';
|
||||
let c:char='我';
|
||||
let c:char='😍';
|
||||
|
||||
//定义布尔类型,占一个字节
|
||||
let ok=true;
|
||||
let ok=false;
|
||||
|
||||
//和架构有关的类型 isize usize, 在多少位的系统上就是多少位的
|
||||
let num:isize=78;
|
||||
let num:usize=34;
|
||||
}
|
||||
|
||||
//复合数据类型
|
||||
fn compound(){
|
||||
//定义一个元组每个元组元素各不相同
|
||||
let tup = ("123", '👀', 34, 45.6f32, b'A', 0x45u16, 0o34);
|
||||
//定义一个元组指定数据类型
|
||||
let tup:(i32,u8,u16) = (1,1,1);
|
||||
//解构元组
|
||||
let (x,y,z) = tup;
|
||||
println!("{} {} {}", x, y, z);
|
||||
//下标获取元组元素
|
||||
let x = tup.0;
|
||||
let y = tup.1;
|
||||
let z = tup.2;
|
||||
//越界访问,编译时即会报错!
|
||||
//let z = tup.3;
|
||||
println!("{} {} {}", x, y, z);
|
||||
}
|
||||
|
||||
fn array(){
|
||||
//定义一个数组,数组元素数据类型必须完全一致
|
||||
let ary = [1,2,3,4];
|
||||
//不仅是数字,可以是任意数据类型
|
||||
let ary = ['💕', '😢', 'A'];
|
||||
//指定数据类型和长度,并初始化
|
||||
let mut ary:[char;3] = ['🙌';3];
|
||||
println!("before {} {} {}", ary[0], ary[1], ary[2]);
|
||||
//使用索引修改数组元素
|
||||
ary[0] = '💕';
|
||||
println!("after {} {} {}", ary[0], ary[1], ary[2]);
|
||||
//越界访问编译报错, 这个检查不一定的可靠的,有可能在逻辑复杂时无法检测到
|
||||
//println!("test {} {} {} {}", ary[0], ary[1], ary[2], ary[3]);
|
||||
}
|
1
vector_learn/.gitignore
vendored
Executable file
1
vector_learn/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
/target
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user