当前位置:首页 > CN2资讯 > 正文内容

rustdesk中继服务器搭建 windows rust服务器直连指令

4小时前CN2资讯


1 安装

在ubuntu下安装,执行:

echo "export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static" >> ~/.bashrc echo "export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup" >> ~/.bashrc source .bashrc curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh #curl https://sh.rustup.rs -sSf | sh # rustup update # 更新rust

安装完毕后刷新环境变量:

source ~/.cargo/env

上面安装好rust的相关软件,然后在.bashrc里面添加环境变量,重启终端生效,或者执行source .bashrc,添加内容如下:

export PATH=$PATH:$HOME/.cargo/bin:$PATH #$

测试:

cargo -V # 查看cargo版本 rustc -V # 查看rust版本

安装 rust 的 vim 插件:

git clone https:///rust-lang/rust.vim.git mv rust.vim .vim/bundle/

VScode上编写、调试rust

1 安装号VScode后,再从商店里面安装两个插件:

  • Rust(rls)
  • Native Debug

重启生效;

2 用vscode打开创建好的rust项目,点击左侧调试按钮,在这里点击创建launch.json文件,选择环境GDB;
3 当前目录下会生成.vscode文件夹,在该目录下手动创建两个文件:tasks.json、launch.json,添加如下内容:
tasks.json

{     "version": "2.0.0",     "tasks": [         {             "label": "build",             "type": "shell",             "command":"cargo",             "args": ["build"]         }     ], "presentation": { "echo": true, "reveal": "always", "focus": false, "panel": "new", "showReuseMessage": true, "clear": false } }

launch.json

{ "version": "0.2.0", "configurations": [ { "name": "Debug", "type": "gdb", "preLaunchTask": "build", "request": "launch", "target": "${workspaceFolder}/target/debug/${workspaceFolderBasename}", "cwd": "${workspaceFolder}" } ] }

添加完毕后,即可点击按钮调试;

2 编写、运行rust程序

  • 创建 文件
  • 编写内容:
fn main() { println!("hello, world!"); }
  • 编译
rustc
  • 运行
./hello
  • 查看文档
cargo doc --open

3 cargo 命令

  • 新建工程
cargo new hello_cargo
  • 编译
cargo build cargo build --release
  • 运行
cargo run
  • 检查
    ``bash
    cargo check
用于检查是否能编译,一般检查语法时用这个命令,速度比 cargo build 快;

4 常用语句

  • 获取键盘输入
fn main() { let mut a = String::new(); std::io::stdin().read_line(&mut a).expect("Failed to read line."); println!("{}",a); }

5 变量与常量

5.1 变量

  • 不可变变量(默认)
let x = 1;

不能第二次赋值

  • 可变变量
let mut x = 1;

5.2 常量

const MAX_POINTS: u32 = 100_000;

5.3 隐藏

fn main() { let x = 5; let x = x + 1; let x = x * 2; println!("{}", x); }

重复创建x变量会隐藏前面的变量,在每次let时,x的类型可以改变,本质是重新创建了一个变量;

6 数据类型

有4种标量类型:整形、浮点型、布尔型、字符型

  • 整形
    不同长度类型:

长度

有符号

无符号

8-bit

i8

u8

16-bit

i16

u16

32-bit

i32

u32

64-bit

i64

u64

128-bit

i128

u128

atch

isize

usize

不同进制的表示方式:

进制

例子


98_222 或者 98222

十六

0xff


0o77


0b1111_0000 或 0b11110000

byte(u8)

b’A’

  • 浮点型
let x = 2.0; // f64,默认64位 let y: f32 = 3.0; // f32
  • 布尔型
let t = true; let f: bool = false;
  • 字符型
let c = 'a';

6.1 数学运算符

Rust 中不同数据类型的常量定义参考这里 Rust 中自带有数学运算,但数学运算需要特定的数据类型,所以在运算前要确保数据类型已经转换为对应的格式:

println!("5+4= {}", 5 + 4); println!("5-4= {}", 5 - 4); println!("5*4= {}", 5 * 4); println!("15/4= {}", 15 / 4); println!("18%4= {}", 18 % 4); println!("2^6 = {}", 2i32.pow(6)); println!("sqrt 9 = {}", 9f64.sqrt()); println!("27 cbrt 9 = {}", 27f64.cbrt()); println!("Round 1.45 = {}", 1.45f64.round()); println!("Floor 1.45 = {}", 1.45f64.floor()); // 返回小于它的最大整数 println!("Ceiling 1.45 = {}", 1.45f64.ceil()); println!("e ^2 = {}", 2f64.exp()); println!("log(2)= {}", 2f64.ln()); println!("log10(2) = {}", 2f64.log10()); println!("90 to Radians = {}", 90f64.to_radians()); println!("PI to Degrees = {}", 3.1415f64.to_degrees()); println!("sin(3.1415) = {}", 3.1415f64.sin()); println!("Max(4,5) = {}", 4f64.max(5f64)); println!("Min(4,5) = {}", 4f64.min(5f64));

7 元组和数组

  • 元组
    元组的元素可以是不同的类型,元组长度固定,一旦声明,其长度不会改变;从元组中取出元素有下面两种方式:
fn main() { let tup = (1, 1.1, 200); // let tup: (i32, f64, u64) = (1, 1.1, 200); let (x, y, z) = tup; let a = tup.1; println!("{}",y); println!("{}",a); }
  • 数组 array
    其元素的类型必须一样:
let a = [1, 2, 3, 4]; let b = ["abc", "abcd", "abcde"]; let c: [i32; 5] = [1, 2, 3, 4, 5]; let d = [3; 5]; println!("{}", d[0]);

访问数组时,如果索引超出了数组的范围,编译可以正常通过,但运行时会报这个访问超出范围的错;

  • slice
    slice 是对数组 array 的切片,所以通过 slice 可以获取 array 的部分或全部的访问权限:
let arr = [1, 2, 3, 4, 5, 6]; let slice_complete = &arr[..]; // 获取全部元素 let slice_middle = &arr[1..4]; // 获取中间元素,最后取得的Slice为 [2, 3, 4] 。切片遵循左闭右开原则。 let slice_right = &arr[1..]; // 最后获得的元素为[2, 3, 4, 5, 6],长度为5。 let slice_left = &arr[..3]; // 最后获得的元素为[1, 2, 3],长度为3。
  • 函数类型
    可以创建变量指向函数:
fn foo(x: i32) -> i32 { x+1 } let x: fn(i32) -> i32 = foo; assert_eq!(11, x(10));

8 函数

注意函数最后一句没有 ‘;’ 号:

fn main() { let x = plus_one(5); println!("{}", x); } // 这里函数的传入参数和传出参数都是所有权,如何使用引用权、借用权在后文描述 fn plus_one(x: i32) -> i32 { x + 1 }

9 控制流

9.1 if

注意 if 的条件不加括号,和c语言不同,其他地方和c语言一样:

fn main() { let x = true; let y = 1; if x && (y == 1) { println!("1") } else if y == 2 { println!("2") } else { println!("3") } }

if 表达式也可用在 let 语句的右侧,但需要在 if 表达式的最后面加上 ‘;’,并且每个条件返回的数据类型要一致,如:

fn main() { let condition = true; let n = if condition { 5 } else { 6 }; println!("{}", n); }

9.2 loop

如果不终止,该循环会一直执行下去:

fn main() { loop { println!("again!"); } }

可以用 break 语句来终止循环,break 后面可以跟上要返回的值:

fn main() { let mut counter = 0; let result = loop { counter += 1; if counter == 10 { break counter * 2; } }; println!("{}", result); }

9.3 while

fn main() { let mut n = 3; while n != 0 { println!("{}", n); n = n - 1; // rust不能使用 n++ 或 n-- } }

9.4 for

类似于 python:

fn main() { let a = [10, 20, 30, 40]; for i in a.iter() { println!("{}", i); // 依次打印 10, 20, 30 40 } }fn main() { for i in (1..5).rev() { println!("{}", i); // 依次打印 4, 3, 2, 1 } }

9.5 break 和 continue

这两个关键字在 rust 中也可以使用,同其他语言一样;

9.6 label

由于经常会使用到嵌套循环,此时如果让 break 和 continue 去直接操作对应层的循环,就可以使用到 label 功能:

'outer: for x in 0..10 { 'inner: for y in 0..10 { if x % 2 == 0 { continue 'outer; } if y % 2 == 0 { continue 'inner; } println!("x: {}, y: {}", x, y); } }
  • 注释
// abcd /* abcd */
  • 新建变量
let x = 20;
  • 打印
println!("x = {}", x); // {}与x对应

10 所有权

先解释以下堆和栈,一般而言,数据占用已知且固定大小,就可以把这类数据存放在栈中,如果数据占用的大小未知,而且是在程序中产生,那么这类数据会放在堆中;栈在内存中是连续的内存空间,用进栈、出栈的方式管理,堆的产生需要向系统申请一段足够大的内存空间,位置不连续;访问堆的时间比栈长;

RUST 的所有权主要用于管理内训,所有权有以下几个规则:

  • Rust中的每一个值都有一个被称为所有者的变量;
  • 值有且只有一个所有者;
  • 当所有者(变量)离开作用域,这个值将被丢弃;

10.1 String 类型

这个类型不仅仅存放在栈上,它的值会存放在堆上,所以能够存储在编译时未知大小的文本;相关的一些使用如下:

let mut s = String::from("hello"); s.push_str(", world!"); println!("{}", s);

10.2 变量与数据交互的方式

10.2.1 方式一:移动

分析几个例子:

  • 移动普通数字变量
let x = 5; let y = x;

在上面的代码中,两个变量的值都为5,因为它们内存大小固定,将存放在栈中,栈里的变量移动时相当于复制;

  • 移动String类型
let s1 = String::from("hello"); let s2 = s1;

String类型的值存放在堆中,所以应该会有深拷贝和浅拷贝的问题,不过在Rust中,这里s2 = s1更像是浅拷贝,但与浅拷贝不同的是这里s1将会变得无效,同时,s1把所有权交给了s2,比如下面这段代码不能运行:

let s1 = String::from("hello"); let s2 = s1; println!("{}", s1);


换句话说,s1把所有权交给了s2,自己就失效了,因为一个值只有一个变量具有所有权;下面这段代码能运行:

let s1 = String::from("hello"); let s2 = s1; println!("{}", s2);
10.2.1 方式二:克隆

克隆也是深拷贝,不仅仅是栈上的数据,堆上的数据也被复制了,如下:

let s1 = String::from("hello"); let s2 = s1.clone(); println!("s1 = {}, s2 = {}", s1, s2);

10.3 所有权与函数

先看两段代码:

fn main() { let s: i32 = 1; fun1(s);// s复制了一份传入函数 // 到这里s还能使用 } fn fun1(arg: i32) { printl!("{}", arg); }fn main() { let s = String::from("hello"); fun2(s);// s传递到函数中,自己销毁了(其实是s对应的栈数据销毁了,复制了一个新的栈数据替代它,并传入函数) // 到这里s不能使用了 } fn fun2(arg: String) { printl!("{}", arg);// arg会销毁 }

对比上面两组代码可以发现,传递变量到函数中,默认使用的是“=(移动)”操作,如果这个变量值存在于栈中(如i32),就相当于复制,如果还存在于堆中(如String),原数据将会被销毁;
对于这种所有权的传递方式,怎么能让传入参数不丢失呢?解决方案是在函数的末尾又传出来,如下:

fn main() { let s1 = String::from("hello"); let s2 = fun1(s1); } fn fun1(s: String) -> String { printl!("{}", s); s }

也可以返回多个参数:

fn main() { let s1 = String::from("hello"); let (s2, len) = fun1(s1); } fn fun1(s: String) -> (String, usize) { printl!("{}", s); let length = s.len(); (s, length) }

10.4 引用和可变引用

上面的函数采用所用权传递,对传入参数影响较大,对此,可以通过引用来解决这个问题,如下:

fn main() { let s1 = String::from("hello"); let len = calculate_length(&s1); println!("The length of '{}' is {}.", s1, len); } fn calculate_length(s: &String) -> usize { s.len() }

引用就是在变量类型的前面加上 & 符号,默认情况下函数就只有使用它的权利,没有更改、销毁的权利,如果要有修改的权利,就是可变引用,如下:

fn main() { let mut s = String::from("hello"); change(&mut s); } fn change(some_string: &mut String) { some_string.push_str(", world"); }

10.5 Slice 类型

11 结构体

11.1 定义、使用结构体

  • 1 定义结构体
struct User { username: String, email: String, sign_in_count: u64, active: bool, }
  • 2 实例化结构体
let user1 = User { email: String::from("[email protected]"), username: String::from("someusername123"), active: true, sign_in_count: 1, };
  • 3 改变成员的值
    如果要改变成员的值,那么在实例化结构体的时候就要定义成 mut 类型的,然后就可以在外部更改:
let mut user1 = User { email: String::from("[email protected]"), username: String::from("someusername123"), active: true, sign_in_count: 1, }; user1.email = String::from("xx");
  • 4 结构体作为函数参数
// 调用函数 let user1 = build_user(String::from(""), String::from("xxx")); // 定义函数 fn build_user(email: String, username: String) -> User { User { email: email, username: username, // 为了简化上面的重复名称,可以替换成下面这样: // email, // username, active: true, sign_in_count: 1, } }
  • 5 如果新建的实例内容和已经存在的实例有重复的,可以用下面的方法简化代码:
// 不简化 let user2 = User { email: String::from("[email protected]"), username: String::from("anotherusername567"), active: user1.active, sign_in_count: user1.sign_in_count, }; // 简化 let user2 = User { email: String::from("[email protected]"), username: String::from("anotherusername567"), ..user1 // ..表示余下的所有其他成员都保持一致 };
  • 6 也可定义与元祖类似的结构体:
struct Color(i32, i32, i32); struct Point(i32, i32, i32); let black = Color(0, 0, 0); let origin = Point(0, 0, 0);

11.2 打印结构体

显然直接用 println!("{}", user1); 是不行的,打印之前需要在开头加上 #[derive(Debug)],并且使用 println!("{:?}", user1); 来打印,打印信息如下:

User { name: "123", email: "345", age: 1, active: true }

也可以改变打印的格式,比如是否换行,这只需要改变打印的参数,将 {:?} 改为 {:#?};

11.3 给结构体添加方法

观察 impl 中各个函数的参数:

#[derive(Debug)] struct Rectangle { width: u32, height: u32, } impl Rectangle { // impl + 结构体名 fn area(&self) -> u32 { // 也可以换成 (&mut self) self.width * self.height } fn can_hold(&self, other: &Rectangle) -> bool { // 可以定义多个方法,self是自身 self.width > other.width && self.height > other.height } } // 可以多处使用 impl 块 impl Rectangle { // 参数中没有self,称关联函数,外部调用是用 :: 符号 fn square(size: u32) -> Rectangle { Rectangle { width: size, height: size } } } fn main() { let rect1 = Rectangle { width: 30, height: 50 }; println!("The area of the rectangle is {} square pixels.",rect1.area()); // 调用关联函数 println!("{:?}", rect1::square(3)); }

12 枚举

12.1 定义枚举

  • 1 定义
enum IpAddrKind { V4, V6, } let four = IpAddrKind::V4; let six = IpAddrKind::V6;
  • 2 作为函数参数
    ``rust
    enum IpAddrKind {
    V4,
    V6,
    }
    fn route(ip_type: IpAddrKind) { }
    route(IpAddrKind::V4);
    route(IpAddrKind::V6);
  • 3 每个成员可以有自己的数据类型
    如:
enum IpAddr { V4(u8, u8, u8, u8), V6(String), } let home = IpAddr::V4(127, 0, 0, 1); let loopback = IpAddr::V6(String::from("::1"));

数据类型也可以是结构体:

struct Ipv4Addr { // --snip-- } struct Ipv6Addr { // --snip-- } enum IpAddr { V4(Ipv4Addr), V6(Ipv6Addr), }
  • 4 也可以用 impl 定义方法
enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), } impl Message { fn call(&self) { // 在这里定义方法体 } } let m = Message::Write(String::from("hello")); m.call();
  • 5 用 option 解决空值问题
enum Option<T> { Some(T), None, }

可以这样赋值:

let some_number = Some(5); let some_string = Some("a string"); let absent_number: Option<i32> = None;

如果变量的值有可能为空,就需要用 Option 来修饰,然后通过判断,只对其不为空的值做处理,如:

let mut a: Option<i32>; //a = None; a = Some(1); // 解析 Option 方法一: if let Some(i) = a { // 这里的 i 就相当于 a 里的值,因为 a 是个Option,值不能直接访问 println!("{}", i); } // 解析 Option 方法二: let v = a.unwrap(); println!("{}", v); // 解析 Option 方法三: match a.as_mut() { Some(v) => println!("{}", *v), None => {}, } // 判断 Option 是否有值 if a.is_some() { println!("有值" ); }
  • 6 Result<T, E> 用于函数的返回,处理错误情况
// 定义 enum Result<T, E> { Ok(T), Err(E), } // 使用 let f = File::open("hello.txt"); // 返回值就是 Result<T, E> 类型 match f { Ok(file) => { println!("File opened successfully."); }, Err(err) => { println!("Failed to open the file."); } }

12.2 match

  • 1 match 用于过滤枚举的值,逻辑像 swich,哪一个条件匹配了就执行该条件后面的语句:
enum Coin { Penny, Nickel, Dime, Quarter, } // 这里可以用 impl 方法把函数绑在 Coin 中 fn value_in_cents(coin: Coin) -> u8 { match coin { Coin::Penny => { println!("Lucky penny!"); 1 }, Coin::Nickel => 5, Coin::Dime => 10, Coin::Quarter => 25, } } fn main() { let coin = Coin::Nickel; println!("{}", value_in_cents(coin)); }
  • 2 枚举还可以嵌套,match 同样可以使用:
#[derive(Debug)] enum UsState { Alabama, Alaska, } enum Coin { Penny, Nickel, Dime, Quarter(UsState), } fn value_in_cents(coin: Coin) -> u8 { match coin { Coin::Penny => 1, Coin::Nickel => 5, Coin::Dime => 10, Coin::Quarter(state) => { println!("State quarter from {:?}!", state); 25 }, } }
  • 3 关于 Option<T>,和枚举一样使用:
fn plus_one(x: Option<i32>) -> Option<i32> { match x { None => None, Some(i) => Some(i + 1), } } let five = Some(5); let six = plus_one(five); let none = plus_one(None);
  • 4 match 需要把所有的情况罗列出来,如果不想把所有的罗列出来,可以使用通配符_,如:
let some_u8_value = 0u8; match some_u8_value { 1 => println!("one"), 3 => println!("three"), 5 => println!("five"), 7 => println!("seven"), _ => (), // 其他情况会自动罗列完 // 如果不罗列完所有情况,编译会查得出来 }

如果很多种情况,而只需要操作其中一种情况,就可以用上面的方式解决,代码像这样:

let some_u8_value = Some(0u8); match some_u8_value { Some(3) => println!("three"), _ => (), }

对此,有一个更简单的方法,就是使用 if let:

# let some_u8_value = Some(0u8); if let Some(3) = some_u8_value { println!("three"); }

12.3 if let简单控制流

如果只想匹配枚举中的单个选项,可以使用 if let 来简化 match,如:

let some_u8_value = Some(0u8); //match some_u8_value { //Some(3) => println!("three"), //_ => (), //} if let Some(3) = some_u8_value { println!("three"); }

13 组织管理

一般的工程项目都会涉及到很多个文件,并且需要在不同的文件中是实现不同的功能,如何将这些文件组织在一起是首先要解决的问题;Rust对此的处理能让项目文件的结构很清晰,各种模块、功能的分类结构和文件夹结构非常相似;

13.1 创建模块、调用模块

在新建的Ruat项目中,只存在 src/main.rs 源程序,我们另外创建一个 my_lib.rs 文件,src 文件结构如下:

── src ├── my_lib.rs └── main.rs

在 my_lib.rs 文件中添加 fun1 函数和 module1 模块,代码如下:
my_lib.rs # 文件名本身就是模块名,如 my_lib,文件里的模块等归属在它下面

pub fn fun1() {} // pub 表示公开,外部能访问,不使用的话默认私有 pub mod module1 { pub mod module1_1 { // 模块可以内嵌 pub fn fun2() {} } pub fn fun3() {} }

在 main.rs 文件中调用 fun1函数 和 module1 中的函数:
main.rs

mod my_lib; // 引用 my_lib.ts 文件这个模块 pub use crate::my_lib::module1; fn main() { my_lib::fun1(); // 调用 my_lib.rs 中的 fun1 函数 module1::module1_1::fun2(); // 调用 my_lib.rs 中的模块 module1::fun3(); }

13.2 模块的结构和文件夹的结构关系

再据一个例子来说明其组织管理和文件夹结构的关系,在上面的代码基础上,在 my_lib 中再添加模块 module2:
my_lib.rs

pub fn fun1() {} // pub 表示公开,外部能访问,不使用的话默认私有 pub mod module1 { pub mod module1_1 { // 模块可以内嵌 pub fn fun2() {} } pub fn fun3() {} pub mod module2;// 需要创建一个 rs 文件来添加里面的内容

然后在 my_lib.rs 的统计目录下创建 my_lib 文件夹,并在 my_lib 文件夹下创建 module2.rs 文件,并添加代码:
my_lib.rs

pub fn fun4() {}

调用方式和前面一样:
main.rs

mod my_lib; pub use crate::my_lib::module2; fn main() { module2::fun4();

该实验的文件结构:

── src ├── my_lib │ └── module2.rs ├── my_lib.rs └── main.rs

13.3 crate、super、use、as关键词

crate 表示根目录
super 表示父目录,也就是父模块
use 使用模块的作用域
as 给作用于提供新名称

use std::io::Result as IoResult;

13.4 创建模块

用下面命令可以单独创建库工程(或像前文一样直接添加 .rs 文件,并在里面实现模块):

cargo new --lib <库名称>

创建完毕后,里面自带测试代码:

#[cfg(test)] mod tests { #[test] fn it_works() { assert_eq!(2 + 2, 4); } }

执行下面命令即可测试:

cargo test //cargo test -- --nocapture

注:库的文档通常写在文档注释中,注释///在任何项之前开始,或//!记录父项;

13.5 使用外部包

13.6 推荐的目录结构

. ├── Cargo.lock ├── Cargo.toml ├── src/ │ ├── lib.rs │ ├── main.rs │ └── bin/ │ ├── named-executable.rs │ ├── another-executable.rs │ └── multi-file-executable/ │ ├── main.rs │ └── some_module.rs ├── benches/ │ ├── large-input.rs │ └── multi-file-bench/ │ ├── main.rs │ └── bench_module.rs ├── examples/ │ ├── simple.rs │ └── multi-file-example/ │ ├── main.rs │ └── ex_module.rs └── tests/ ├── some-integration-tests.rs └── multi-file-test/ ├── main.rs └── test_module.rs

Cargo.toml 与 Cargo.lock 存储在项目的根目录中。
源代码进入 src 目录。
默认库文件是 src/lib.rs。
默认的可执行文件是 src/main.rs。
其他可执行文件可以放入 src/bin/*.rs。
集成测试进入 tests 目录(单元测试进入他们正在测试的每个文件中)。
示例可执行文件放在 examples 目录中。
基准测试进入 benches 目录。

14 数据集合

Rust 中广泛使用的数据集有3类:Vector、String、HashMap;这些数据集的内容一般存放在堆上,由程序运行时动态分配;

14.1 Vector

  • 新建 vector:
let v: Vec<i32> = Vec::new(); // 创建空的vector,类型i32 let v: Vec<i32> = Vec::with_capacity(10); // 和上面new一样,但会预先分配10个成员空间,效率稍微比new高点 let v = vec![1, 2, 3]; // 新建一个拥有值1、2、3的 Vec<i32>
  • 更新 vector :
let mut v = Vec::new(); v.push(5); // 增加元素 v.push(6); v.push(7); v.push(8);
  • 读取 vector 的元素
    读取有两种方式:索引法、get方法,如果超出了范围,索引法会在程序运行时报错,而 get 方法会返回 None,在实际运用中考虑这两者的区别;
let v = vec![1, 2, 3, 4, 5]; let third: &i32 = &v[2]; println!("The third element is {}", third); match v.get(2) { Some(third) => println!("The third element is {}", third), None => println!("There is no third element."), }
  • 遍历 vector
let v = vec![100, 32, 57]; for i in &v { println!("{}", i); }

也可以在遍历的时候改变它的值:

let mut v = vec![100, 32, 57]; for i in &mut v { *i += 50; }
  • 在 vector 中存放不同类型的值可以搭配枚举来实现:
enum SpreadsheetCell { Int(i32), Float(f64), Text(String), } let row = vec![ SpreadsheetCell::Int(3), SpreadsheetCell::Text(String::from("blue")), SpreadsheetCell::Float(10.12), ];

14.2 String

  • 新建字符串string
// 创建空的string let mut s = String::new(); // 从 str -> string let data = "initial contents"; let s = data.to_string(); // 该方法也可直接用于字符串字面值: let s = "initial contents".to_string(); // 从 string -> string let s = String::from("initial contents");
  • 更新string
let mut s = String::from("foo"); s.push_str("bar"); // 追加字符串 s.push('l'); // 追加字符
  • 拼接string
let s1 = String::from("Hello, "); let s2 = String::from("world!"); let s3 = s1 + &s2; // 注意 s1 被移动了,不能继续使用 let s1 = String::from("tic"); let s2 = String::from("tac"); let s3 = String::from("toe"); let s = format!("{}-{}-{}", s1, s2, s3); // 多个字符串连接
  • 索引string
let s1 = String::from("hello"); //let s1 ="sdfaasdf"; let h = s1[0]; // 错误!不能这样索引 println!("{}, {}", &hello[0..1], hello);
  • 遍历string
let s = String::from("hello world!"); for c in s.chars() { println!("{}", c); // 遍历每一个char,结果是字符 } for c in s.bytes() { println!("{}", c); // 遍历每一个byte,结果是u8类型的数字 }
  • string 和 int 互换
let int_value = 5; let string_value = int_value.to_string();//int to String let back_int = string_value.parse::<i32>().unwrap();//类型可以换乘下面的 u32 、i16 等 //let back_int = string_value.parse::<u32>().unwrap(); //let back_int = string_value.parse::<i16>().unwrap();
  • string 的常用方法:
    参考这里

方法

原型

说明

new()

pub const fn new() -> String

创建一个新的字符串对象

to_string()

fn to_string(&self) -> String

将字符串字面量转换为字符串对象

replace()

pub fn replace<'a, P>(&'a self, from: P, to: &str) -> String

搜索指定模式并替换

as_str()

pub fn as_str(&self) -> &str

将字符串对象转换为字符串字面量

push()

pub fn push(&mut self, ch: char)

再字符串末尾追加字符

push_str()

pub fn push_str(&mut self, string: &str)

再字符串末尾追加字符串

len()

pub fn len(&self) -> usize

返回字符串的字节长度

trim()

pub fn trim(&self) -> &str

去除字符串首尾的空白符

split_whitespace()

pub fn split_whitespace(&self) -> SplitWhitespace

根据空白符分割字符串并返回分割后的迭代器

split()

pub fn split<'a, P>(&'a self, pat: P) -> Split<'a, P>

根据指定模式分割字符串并返回分割后的迭代器。模式 P 可以是字符串字面量或字符或一个返回分割符的闭包

chars()

pub fn chars(&self) -> Chars

返回字符串所有字符组成的迭代器

14.3 HashMap

HashMap 由 键-值组成,键类型是string,值类型是i32;

  • 新建HashMap
use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from("Blue"), 10); // Blue 是键,10 是值 scores.insert(String::from("Yellow"), 50);
  • 利用 vector 生成 HashMap:(其实是 vector -> 元祖 -> HashMap,这两个阶段用到了 zip 和 collect() 方法)
use std::collections::HashMap; let teams = vec![String::from("Blue"), String::from("Yellow")]; let initial_scores = vec![10, 50]; let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect(); // HashMap<_, _> 类型注解不能少
  • 也可以用插入 vector 的方法生成 HashMap:
use std::collections::HashMap; let field_name = String::from("Favorite color"); let field_value = String::from("Blue"); let mut map = HashMap::new(); map.insert(field_name, field_value); // 这里 field_name 和 field_value 不再有效,所有权交给了map
  • 访问 HashMap 中的值
    可以通过 get 方法并提供对应的键来从哈希 map 中获取值,如果在里面没有对用的值,get 会返回 None:
use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from("Blue"), 10); scores.insert(String::from("Yellow"), 50); let team_name = String::from("Blue"); let score = scores.get(&team_name); // 根据健找值 for (key, value) in &scores { println!("{}: {}", key, value); // 访问 HashMap 中的每一对键值 }
  • 更新 HashMap
    1 覆盖一个值
    如果我们插入了一个键值对,接着用相同的键插入一个不同的值,与这个键相关联的旧值将被替换:
use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from("Blue"), 10); scores.insert(String::from("Blue"), 25); println!("{:?}", scores); //这会打印出{"Blue": 25},原始的值10则被覆盖了。
  • 2 只在键没有对应值时插入
    如果 HashMap 中不存在这个键值就插入,否则忽略:
use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from("Blue"), 10); scores.entry(String::from("Yellow")).or_insert(50); scores.entry(String::from("Blue")).or_insert(50); println!("{:?}", scores); // 打印出 {"Yellow": 50, "Blue": 10}
  • 3 根据旧值更新一个值
    下面代码是计数文本中每一个单词分别出现了多少次:
use std::collections::HashMap; let text = "hello world wonderful world"; let mut map = HashMap::new(); for word in text.split_whitespace() { let count = map.entry(word).or_insert(0); *count += 1; } println!("{:?}", map); // 打印结果 {"world": 2, "hello": 1, "wonderful": 1}

15 迭代器 Iterarot

Rust 语言中的集合包括 数组( array )、向量( Vect! )、哈希表( map )等,我们可以简单的使用 iter() 和 next() 方法来完成迭代:

fn main() { //创建一个数组 let a = [10,20,30]; let mut iter = a.iter(); // 从一个数组中返回迭代器 println!("{:?}",iter); //使用 next() 方法返回迭代器中的下一个元素 println!("{:?}",iter.next()); println!("{:?}",iter.next()); println!("{:?}",iter.next()); println!("{:?}",iter.next()); for i in iter { // 遍历 iterarot } for (index, data) in iter.enumerate() { // 遍历 iterarot 并携带 索引 } }

输出结果:

Iter([10, 20, 30]) Some(10) Some(20) Some(30) None
  • 迭代器类型常用的有3种:

方法

描述

iter()

返回一个只读可重入迭代器,迭代器元素的类型为 &T(借用)

into_iter()

返回一个只读不可重入迭代器,迭代器元素的类型为 T(转移所有权)

iter_mut()

返回一个可修改可重入迭代器,迭代器元素的类型为 &mut T(可变借用)

使用如下:

let names = vec!["简单教程", "简明教程", "简单编程"]; for name in names.iter() { match name { &"简明教程" => println!("我们当中有一个异类!"), _ => println!("Hello {}", name), } } println!("{:?}",names); // 迭代之后可以重用集合 }

16 闭包

闭包就是在一个函数内创建立即调用的另一个函数,没有函数名称,闭包不用声明返回值,但它却可以有返回值,闭包有时候有些地方又称之为 内联函数。这种特性使得闭包可以访问外层函数里的变量;
闭包格式:

||{ // 闭包的具体逻辑 }

创建闭包,并赋给一个变量:

fn main(){ let is_even = |x| { x%2==0 }; let no = 13; println!("is even ? {}", is_even(no)); // 判断是否为偶数 }

闭包也可以访问外部的变量:

fn main(){ let val = 10; let closure2 = |x| { x + val // 访问作用域外部的变量 }; println!("{}",closure2(2)); }

17 指针

17.1 原始引用

Rust 支持两种原始引用:

  • 不可变原始指针 *const T
  • 可变原始指针 *&mut T

用 as 操作符可以将引用转为原始指针:

let mut x = 10; let ptr_x = &mut x as *mut i32; let y = Box::new(20); let ptr_y = &*y as *const i32; // 原生指针操作要放在unsafe中执行 unsafe { *ptr_x += *ptr_y; } assert_eq!(x, 30);

17.2 函数指针

函数指针可以作为函数的参数和返回值;
作为参数:

pub fn math(op: fn(i32, i32) -> i32, a: i32, b: i32) -> i32{ op(a, b)/// 通过函数指针调用函数 } fn sum(a: i32, b: i32) -> i32 { a + b } let a = 2; let b = 3; assert_eq!(math(sum, a, b), 5);

作为返回值:

fn is_true() -> bool { true } fn true_maker() -> fn() -> bool { is_true } // 函数的返回值是另外一个函数 assert_eq!(true_maker()(), true); // 通过函数指针调用函数

17.3 智能指针

  • Box<T>是指向类型为T的堆内存分配值的智能指针;
  • 当 Box<T> 超出作用域范围时,将调用其析构函数,销毁内部对象,并自动释放内存。
  • 可以通过解引用操作符来获取Box<T>中的T
#[derive(Debug, PartialEq)] struct Point { x: f64, y: f64, } let box_point = Box::new(Point { x: 0.0, y: 0.0 }); let unboxed_point: Point = *box_point; assert_eq!(unboxed_point, Point { x: 0.0, y: 0.0 });

y = Box::new(x) // 创建一个指向x的指针
std::mem::drop(x) // 手动提前释放x

Box<T>Rc<T>允许相同数据有多个所有者;Box<T>和RefCell<T>有单一所有者。
RefCell<T>

18 泛型

18.1 枚举泛型

enum Option<T> { Some(T), None, }

18.2 函数泛型

fn takes_anything<T>(x: T) { // ... }

18.3 结构体泛型

struct Point<T>{ x:T, y:T, } // 给结构体定义方法 impl <T> Point<T>{ fn get_x(&self)->&T{ // 泛型参数都可以使用该方法 return &self.x; } } impl Point<f32>{ fn distiance_from_origin(&self)->f32{ // 只有f32类型参数可以使用该方法 return (self.x.powi(2) + self.y.powi(2)).sqrt(); } } fn main() { let int:Point<i32> = Point{x:4, y:4}; let fl:Point<f32> = Point{x:2.0, y:2.0}; println!("{}, {}, {}, {}", int.x, fl.y, int.get_x(), fl.distiance_from_origin()); }

18.4 trail

trail 用来实现多态。类似C++中的接口;

  • 基本使用
pub trait Summary{ fn summarize(&self)->String; // 在这里可以实现该方法,作为默认方法 } pub struct NewsArticle { pub headline: String, pub location: String, pub author: String, pub content: String, } impl Summarizable for NewsArticle { // 有这句话才会将trail应用到结构体中 fn summary(&self) -> String { format!("{}, by {} ({})", self.headline, self.author, self.location) } }
  • trail 作为参数
// 续"基本使用"的代码 pub fn notify(item:impl Summary){ //在 notify 函数体中,可以调用任何来自 Summary trait 的方法 println!("参数:{}", item.summarize()) } fn main() { let article = NewsArticle { headline: String::from("NewsArticle_headline"), location: String::from("Pittsburgh, PA, USA"), author: String::from("Iceburgh"), content: String::from("The Pittsburgh Penguins once again are the best hockey team in the NHL."), }; notify(article); //值的所有权 }

此外,Rust还提供了另一种语法糖来,即Trait限定,我们可以使用泛型约束的语法来限定Trait参数;

pub fn notify<T: Summary>(item: T) { println!("Breaking news! {}", item.summarize()); }

这样的语法糖可以在多个参数的函数中帮助我们简化代码,下面两行代码就有比较明显的对比:

pub fn notify(item1: impl Summary, item2: impl Summary) { pub fn notify<T: Summary>(item1: T, item2: T) {

如果某个参数有多个trait限定,就可以使用+来表示

pub fn notify<T: Summary + Display>(item: T) { /* 或者写成这样更漂亮 fn some_function<T, U>(t: T, u: U) -> i32 where T: Display + Clone, U: Clone + Debug {*/
  • trail 作为返回值
// 续"基本使用"的代码 fn returns_summarizable() -> impl Summary { NewsArticle { headline: String::from("NewsArticle_headline"), location: String::from("Pittsburgh, PA, USA"), author: String::from("Iceburgh"), content: String::from("The Pittsburgh Penguins once again are the best hockey team in the NHL."), } } fn main() { let al = returns_summarizable(); println!("{}, {}", al.summarize(), al.ari_authon()); }

19 多线程

19.1 创建多线程

创建线程使用的闭包:

use std::thread; use std::time::Duration; fn main() { let handle = thread::spawn(|| { for i in 1..10 { println!("hi number {} from the spawned thread!", i); thread::sleep(Duration::from_millis(1)); } }); for i in 1..5 { println!("hi number {} from the main thread!", i); thread::sleep(Duration::from_millis(1)); } handle.join().unwrap(); }

输出:

hi number 1 from the main thread! hi number 2 from the main thread! hi number 1 from the spawned thread! hi number 3 from the main thread! hi number 2 from the spawned thread! hi number 4 from the main thread! hi number 3 from the spawned thread! hi number 4 from the spawned thread! hi number 5 from the spawned thread! hi number 6 from the spawned thread! hi number 7 from the spawned thread! hi number 8 from the spawned thread! hi number 9 from the spawned thread!

19.2 线程与 move 闭包

如果要把外部的数据出传入到线程,那么就需要使用 move 闭包,会把数据的所有权传递给该线程:

use std::thread; fn main() { let v = vec![1, 2, 3]; let handle = thread::spawn(move || { println!("Here's a vector: {:?}", v); }); // v 在这里不能使用了,所有权已传递给线程 handle.join().unwrap(); }

19.3 消息传递

线程间通信采用的信息传递方式,一个生产者(发送),一个消费者(接收),相当于线程间的通道,下面例子采用 mpsc,mpsc 是多个生产者,单个消费者通道,如:

  • 单线程 - [单发送 - 单接收]
use std::thread; use std::sync::mpsc; fn main() { let (tx, rx) = mpsc::channel(); thread::spawn(move || { let val = String::from("hi"); tx.send(val).unwrap(); // val 在这里就不能使用了,所有权在上一步就传递给了send }); let received = rx.recv().unwrap(); println!("Got: {}", received); }
  • 单线程 - [多发送 - 多接收]
use std::thread; use std::sync::mpsc; use std::time::Duration; fn main() { let (tx, rx) = mpsc::channel(); thread::spawn(move || { let vals = vec![ String::from("hi"), String::from("from"), String::from("the"), String::from("thread"), ]; for val in vals { tx.send(val).unwrap(); thread::sleep(Duration::from_secs(1)); } }); for received in rx { println!("Got: {}", received); } }
  • 多线程 - [多发送 - 多接收]
# use std::thread; # use std::sync::mpsc; # use std::time::Duration; # # fn main() { // --snip-- let (tx, rx) = mpsc::channel(); let tx1 = mpsc::Sender::clone(&tx); thread::spawn(move || { let vals = vec![ String::from("hi"), String::from("from"), String::from("the"), String::from("thread"), ]; for val in vals { tx1.send(val).unwrap(); thread::sleep(Duration::from_secs(1)); } }); thread::spawn(move || { let vals = vec![ String::from("more"), String::from("messages"), String::from("for"), String::from("you"), ]; for val in vals { tx.send(val).unwrap(); thread::sleep(Duration::from_secs(1)); } }); for received in rx { println!("Got: {}", received); } // --snip-- # }

19.4 共享状态并发

多个线程拥有数据的所有权

use std::sync::{Mutex, Arc}; use std::thread; fn main() { let counter = Arc::new(Mutex::new(0)); let mut handles = vec![]; for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!("Result: {}", *counter.lock().unwrap()); }

20 面向对象

参考

21 智能指针

参考

22 测试

22.1 模块测试和集成测试

Rust 的测试分为单元测试和集成测试,对应的测试代码只能在 cargo test 命令下编译运行;

  • 单元测试
    单元测试代码一般存在于源码中,测试模块需要用 #[cfg(test)] 修饰,此模块中的测试函数需要用 #[test] 修饰,如:
pub fn add_two(a: i32) -> i32 { a + 2 } #[cfg(test)] //标记该模块为测试模块 mod tests { use super::*; #[test] // 标记该函数为测试函数 // #[should_panic] // 如果出错了也不终止程序 // #[ignore] // 测试时忽略该函数 fn add_two_and_two() { assert_eq!(4, add_two(2)); } }
  • 集成测试
    集成测试需要在项目根目录创建 tests 文件夹,在该文件下创建测试文件 ,并在里面编写测试函数,如:
use adder; // 导入源程序中需要测试的模块 #[test] fn it_adds_two() { assert_eq!(4, adder::add_two(2)); }

22.2 测试代码常用关键字

assert!(xxx); // 如果值为 true,则通过 assert_eq!(xxx, xxx); // 如果两个值相等,则通过 assert_ne!(xxx ,xxx); // 如果两个值不相等,测通过 panic!("...{}", num); // 终止程序并打印信息

也可以用 Result<T, E> 作为函数的返回来编写测试,如:

#[cfg(test)] mod tests { #[test] fn it_works() -> Result<(), String> { if 2 + 2 == 4 { Ok(()) } else { Err(String::from("two plus two does not equal four")) } } }

22.3 测试命令

cargo test // 编译运行测试程序 cargo test [函数名] // 测试某个函数,只要函数名包含该字符串的都会被测试 cargo test -- --test-threads=1 // 单线程测试,因为默认情况是多个测试同时进行 cargo test -- --nocapture // 允许显示其他打印信息,比如 println! ,测试通过的也要打印出信息 cargo test -- --ignored // 让代码中用 #[ignore] 修饰过的函数也参与测试

暂时未分类

交换两个变量

std::mem::swap(&mut a, &mut b); // 相当于交换两者的所有权,数据区域的内存地址没变

退出程序

std::process::exit(0);

将一个 String 变成 &str 用 &* 符号

fn use_str(s: &str) { println!("I am: {}", s); } fn main() { let s = "Hello".to_string(); use_str(&*s); }

实现链表的例子

pub struct ListNode { pub val: i32, pub next: Option<Box<ListNode>> } impl ListNode { fn new(val: i32) -> Self { ListNode { next: None, val } } } pub fn function(l1: Option<Box<ListNode>>) -> Option<Box<ListNode>> { let mut res = ListNode::new(0); let mut res_temp = &mut res; let mut p_l1 = &l1; while p_l1.is_some() { if let Some(node) = p_l1 { p_l1 = &node.next; } res_temp.next = Some(Box::new(ListNode::new(0))); res_temp = res_temp.next.as_mut().unwrap(); } res.next }


    你可能想看:

    扫描二维码推送至手机访问。

    版权声明:本文由皇冠云发布,如需转载请注明出处。

    本文链接:https://www.idchg.com/info/30403.html

    分享给朋友:

    “rustdesk中继服务器搭建 windows rust服务器直连指令” 的相关文章