rust学习文档
cargo new hello:创建hello工程,cargo工具会自动创建Cargo.toml配置文件和src目录,src中有main.rs文件;
// rust入口函数main
fn main() {
// println!是一个打印的宏,不是函数;
println!("Hello, world!");
}
cargo run:运行;运行后会有target目录,其中不含了最后的可执行文件;
cargo build:只编译不运行;
cargo check:校验语法;
// 定义变量是用let关键字
// 定义不可变变量
let a = 1; // 自动推导类型
let b : u32 = 2; // 指定类型为u32
// b = 3; 不可变变量不能赋值,编译错误
println!("a = {}", a);
// 定义可变变量
let mut c = 1;
c = 2;
println!("c = {}", c);
// 变量隐藏
let b : f32 = 1.1; // 将上面的变量b隐藏了;c++中应该会重定义编译错误;
println!("b = {}", b);
// 常量,直接使用const关键字
const MAX_POINT : u32 = 100000;
println!("MAX_POINT = {}", MAX_POINT);
// bool
let is_true : bool = true;
let is_false = false; // 自动推导
println!("bool var = {}, {}", is_true, is_false); // 结果: bool var = true, false
// char: rust中char是32位的; 因此一个字符可以是汉字或者字母
let a = 'a';
let b = ‘你’;
printlen!("b = {}", b);
// 数字类型:i8, i16, 132, i64, u8, u16, u32, f32, f64
let c: i8 = -111;
println!("c = {}", c);
// 自适应类型:类型长度随着系统不同而不同,isize, usize
println!("max = {}", usize::max_value()); // 打印系统usize的最大数字
// 数组
// 定义方式:[Type; size],中间用分号隔开
let arr : [u32; 5] = [1, 2, 3, 4, 5]; // 用中括号初始化
prinlen!("arr[0] = {}", arr[0]);
// 注意,size也是数组类型的一部分,比如一个函数接收size为三的数组,就不能将上面的arr传进去,编译不通过;
// show(arr); 编译错误,不能将size不为3的数组传入show中;
fn show(arr:[u32; 3]) {
for i in &arr {
println!("{}", i);
}
}
// 元组
let tup: (i32, f32, char) = (-3, 3.69, '好'); // 使用圆括号初始化;
println!("{}, {}, {}", tup.0, tup.1, tup.2); // 使用下标访问
// 拆解元组,分别使用元组元素
let (x, y, x) = tup;
prinfln!("{}, {}, {}", x, y, z);
// if
let y = 1;
if y == 1 {
println!("y = 1");
}
// if-else
if y == 1 {
println!("y = 1");
} else {
println!("y != 1");
}
// if - else if - else
if y == 1 {
println!("y = 1");
} else if y == 0 {
println!("y = 0");
} else if y == 2 {
println!("y = 2");
} else {
println!("other");
}
// let中使用if, if也是有返回值的,
let condition = true;
let x = if condition {
5 // 不能加分号, 两个分支中必须是同一类型,因为编译时自动类型推导会出错;
} else {
6
}; // 这时候if-else是表达式,所以需要加分号
// loop : 循环
let mut counter = 0;
loop {
println!("in loop");
if counter == 10 {
break; // 退出循环
}
counter += 1;
}
// loop返回值
let res = loop {
counter += 1;
if counter == 20 {
break counter * 2;
}
}; // 这时候loop是表达式,所以需要加分号;
println!("res = {}", res); // res = 40
// while循环
let mut i = 0;
while i != 10 {
i += 1;
}
println!("i = {}", i); // i = 10
// for循环
let arr:[u32; 5] = [1, 2, 3, 4, 5];
// 使用数组的迭代器遍历
// for ele in arr.iter() {
// 使用引用遍历数组
for ele in &arr { // 必须加引用,可能for in在定义时就是只接收引用,不能接收值传递
println!("element = {}", ele);
}
语句:执行一些操作,但是不返回值;
let y = 1; // 语句,不返回值
// let x = (let y = 1); // let y = 1是语句,没有返回值;
表达式:会计算一些值,有返回值
let x = {
let y = 1;
y + 1
};
println!("x = {}", x);
使用fn关键字定义函数
// 无参函数
fn other_fun() {
println!("This is a function");
}
// 有参函数,变量:type
fn oterh_fun1(a: i32, b: u32) {
println!("a = {}, b= {}", a, b);
}
// 有返回值函数,返回i32类型的值
fn oterh_fun2(a: i32, b: i32) -> i32{
let result = a + b;
// 使用return返回
return result;
// 直接返回,但是不能有分号
result
// 不使用中间变量,常用写法
a + b
}
fn main() {
other_fun();
let a: i32 = -1;
let b: u32 = 2;
other_fun1(a, b );
let c: i32 = 9;
let r = other_fun2(a, c);
println!("r = {}", r);
}
1. rust通过所有权机制来管理内存,编译器根据所有权规则对内存进行检查
2. 堆和栈:概念和C++通用;
3. 作用域:{}
4. String内存回收:离开作用域会调用drop方法,类似C++的析构函数,String的结构也和C++类似;
5. 移动(move)
{
let s1 = String::from("hello");
println!("s1 = {}", s1);
let s2 = s1; // 执行move语句,所有权赋给了s2;
println!("s2 = {}", s2);
// println!("s1 = {}", s1); // s1已经没有了所有权
// 问题:如果s2的生命周期比s1短,s2 drop后,所有权会归还s1吗?
}
6. clone trait
{
let s1 = String::from("hello");
let s2 = s1.clone(); // clone语句,类似深拷贝
println!("s2 = {}", s2);
println!("s1 = {}", s1);
}
7. 栈上数据拷贝:栈上数据执行的都是深拷贝;
// copy trait,copy trait直接调用的是clone,它是一种标记,表明执行的深拷贝,即拷贝之后可以使用原变量;
// 常用的具有copy trait有:所有的整数、浮点型、布尔值、字符类型char、元组
{
let a = 1;
let b = a;
println!("a = {}, b= {}", a, b); // 栈上的数据执行的是深拷贝,
}
8. 函数和作用域
fn func(str: String) {
println!("{}", str);
}
fn func2(str: String) -> String {
println!("{}", str);
str
}
fn main() {
let s = String::from("hello");
func(s);
// println!("{}", s); // s所有权转移了(move语句),离开函数作用域后drop掉了,如果是copy语义就可以继续使用;
let s1 = String::from(""hello);
let s2 = func2(s1);
println!("{}", s2); // 所有权返回给s2了;
}
copy trait和clone trait的区别
fn func(str: &String) -> usize {
str.len()
}
// 可变引用,借用
fn modify_s(s : &mut String) {
s.push_str(", world");
}
fn main() {
// 1. 引用
let s1 = String::from("hello");
// let len = func(s1); // 编译不过
let len = func(&s1); // 需要加引用符&
println!("len = {}", len);
println!("s1 = {}", s1); // 传的是s1的引用,所有权没有转移,s1依然可以使用;
// 2. 取变量引用
let s2 = &s1; // 取s2为s1引用
let len2 = func(s2);
println!("s2 = {}", s2);
// 3. 借用,可变引用
let mut s1 = String::from("hello"); // 变量隐藏,可以定义重名的;定义为可变的
modify_s(&mut s1); // 加mut关键字,借用
println!("s1 = {}", s1);
// 4. 在可变引用都定义和访问之间,不能有其他都可变/不可变引用访问
let mut s1 = String::from("hello");
let s = &mut s1;
modify_s(s);
// println!("s1 = {}", s1); // 在可变引用s最后一次访问之前,不能访问s1或s1都引用;
println!("s = {}", s);
}
// 5. 悬垂引用
fn dangle() -> &String {
let s = String::from("hello");
&s
}
fn main() {
let ref = dangel(); // 悬垂引用,因为返回的s已经drop了;和C++中悬垂指针/悬垂引用一样;
}
slice 不是具体的类型,它是一种概念,表示某个数据的存储的一部分值;
fn main () {
// 1. 字符串slice是String中一部分值的引用;
let s = String::from("hello world");
// h和w就是一个slice
let h = &s[0..5]; // ..表示区间,此为左闭右开
let h = &s[0..=4]; // 左闭右闭
let h = &s[..=4]; // 开头不写默认是0
let h = &s[..5]; // 同上
println!("h = {}", h); // 打印hello
let w = &s[6..11];
let w = &s[6..=10];
let w = &s[6..];
printlen!("s = {}", w); // 打印world
let w = &s[..]; // 打印hello world
// slice的边界必须是完整的utf-8
let ss = String::from("你好");
// let w1 = &ss[0..1]; // 编译报错,只取了中文"你"的一半,中文在utf-8中占两个字节;
// 2. 字面值就是slice,如下s3
let s3 = "hh"; // 它的类型同时也是&str,不可变引用
// 3. 其他类型slice
let a = [1, 2, 3, 4]; // 定义数组a;
let sss = &a[1..3]; // 定义slice sss
// println!("sss = {}" sss); // 编译报错
println!("sss[0] = {}", sss[0]); // 打印2
println!("sss[1] = {}", sss[1]); // 打印3
println!("len = {}", sss.len()); // 打印长度
}
有三种结构体:具名结构体、元组结构体和单元结构体
fn main() {
// 1. 定义结构体
struct User {
name : String,
count: String,
nonce: u64,
active: bool,
}
// 2. 创建结构体
let mut xiaoming = User {
name: String::from("xiaoming"),
count: String::from("80001000");
nonce: 1000,
active: true,
};
// 3. 修改结构体
xiaoming.nonce = 20000;
// 4. 参数名字和字段名字同名的简写方法
let name = String::from("xiaoxiao");
let count = String::from("98907987");
let nonce = 20000;
let active = false;
// let user1 = User {
// name: name,
// count: count,
// nonce: nonce,
// active: active,
//};
// 问题:前面的常量name等在user1定义后,所有权有没有转移;
// 或者如下方法,可以省略名字
let user1 = User {
name,
coutn,
nonce,
active,
};
// 5. 从其他结构体创建实例
// 可以采用一般的创建方法,或者简写
let user2 = User {
name: String::from("user2"),
..user1 // 其他需要从user1赋值的,直接用简写
};
println!("name = {}", user2.name);
println!("nonce = {}", user2.nonce);
// 6. 元组结构体
// (1) 没有字段名字
// (2) 使用圆括号
stuct Point(i32, i32);
let a = Point(10, 20);
let b = Point(30, 11);
// 访问元组结构体,使用0,1等下标访问
printtln!("a.0 = {}, a.1={}", a.0, a.1);
// 7. 没有任何字段的类单元结构体
struct A{}; // 可以为结构体实现一些函数,类似C++中一个类有成员函数但是没有成员变量
// 8. 打印结构体,自动推导结构体样式,需要两步:
// (1) 定义结构体时加上#[derive(debug)]
// (2) 打印结构体是在大括号内加上:?
#[derive(debug)]
struct Person {
name: String,
age: u32,
}
let person = Person {
name: String::from("zhangsan"),
age: 18,
};
println!("person = {:?}", person); // 加上:?,打印结果:xiaob = Person {name: "zhangsan", age: 18}
println!("person = {:#?}", person); // 加上:#?,打印结果会自动换行,如下:
// person = Person {
// name: "zhangsan",
// age: 18,
// }
}
#[derive(Debug)] // 这样可以自动推导打印格式
struct Dog {
name: String,
weight: f32,
height: f32,
}
// 实现Dog中的方法
impl Dog {
// self参数类似C++中成员函数的this参数; 返回字面值引用
fn get_name(&self) -> &str {
&(self.name[..])
}
fn get_weight(&self) ->f32 {
self.weight
}
// 定义Dog中函数(不加self),类似C++ static成员函数
fn show() {
println!("oh oh oh");
}
}
// 也可以分开实现方法
impl Dog {
fn get_height(&self) ->f32 {
self.height
}
}
fn main() {
let dog = Dog {
name: String::from("wangcai");
weight: 100.0,
height: 70.5,
};
println!("dog = {:#?}", dog); // 使用自动推导来打印结构体;
println!("name = {}", dog.get_name()); // 调用get_name方法;
// 调用静态方法
Dog::show();
}
模式匹配:使用match必须匹配玩值的所有情况;
fn main {
// 1. 类似C++的定义方式
enum IpAddrKind {
V4,
V6,
}
struct IpAddr {
kind: IpAddrKind,
address: String,
}
let i1 = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
let i2 = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};
// 2. rust语言提倡的定义方式:定义好子类型,如V4(String),并且子类型还可以具有值
enum IpAddr2 {
V4(String),
V6(String),
};
let i1 = IpAddr2::V4(String::from("127.0.0.1")); // 子类型IpAddr2::V4,值为"127.0.0.1"
let i2 = IpAddr2::V6(String::from("::1"));
// 3. 子类型可以是不同都类型
// 和C++的枚举不一样,c++中枚举都是一样的类型
enum Addr3 {
V4(u8, u8, u8, u8),
V6(String),
}
let i1 = Addr3::V4(127, 0, 0, 0);
let i2 = Addr3::V6(String::from("::1"));
}
// 4. 经典用法
enum Message {
Quit, // 等同于类单元结构体
Move{x: i32, y: i32}, // Move是一个结构体
Write(String), // 元组结构体
Change(i32, i32, i32),
}
// 5. 枚举类型的方法及匹配机制,必须匹配完所有情况,否则编译出错
impl Message {
fn prin(&self) {
match self {
Message::Quit => println!("Quit"),
Message::Move{x, y} =>println!("Move x = {}, y = {}", x, y), // Move是结构体,使用花括号
Message::Change(a, b, c) => println!("Change a = {}, b = {}, c = {}", a, b, c),
Message::Write(s) => println!("Write s = {}", s),
_ => println!("Write") // 下划线表示default
}
}
}
fn main() {
let quit = Message::Quit;
quit.prin();
let mo = Message::Move{x: 10, y: 20};
mo.prin();
let wr = Message::Write(String::from("write"));
wr.prin();
let ch = Message::Change(1, 2, 3);
ch.prin();
}
Option是标准库定义的一个枚举类型;
enum Option {
Some(T),
None,
}
使用option的方式:
fn main() {
let some_num = Some(5);
let some_str = Some(String::from("hello"));
let absent_num : Option<i32> = None;
let x: i32 = 5;
let y:Option<i32> = Some(3);
let mut tmp = 0;
// 取Option中的值;
match y {
Some(i) => {tmp = i;},
None => {println!("Do nothing");},
}
let sum = x + tmp;
println!("sum = {}", sum);
let res = plus_one(y);
match res {
None => {println!("Nothing");},
Some(i) => {println!("res = {}", i);},
}
// 使用if else代替match
// let Some(value) = plus_one(y)是一个布尔表达式,赋值成功返回true,否则false
if let Some(value) = plus_one(y) {
println!("value = {}", value);
} else {
println!("Nothing");
}
}
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i+1),
}
}
fn main() {
// 1. 创建vector
let mut v:Vec<i32> = Vec::new();
v.push(3);
// 2. 创建vector,并初始化,使用中括号初始化
let v = vec![1, 2, 3];
// 3. 丢弃vector,出作用域后自动丢弃
{
let v1 = vec![1, 2];
}
// 4. 读取元素
let one: &i32 = &v[0]; // 使用引用
println!("one = {}", one); // 自动解引用
println!("one = {}", *one); // 使用*解引用
// 使用match读取数据,(推荐方法,避免数组越界)
match v.get(1) {
Some(value) => println!("value = {}", value),
_ => println!("Do nothing"),
}
// 5. 更新元素
let mut v2:Vec<i32> = Vec::new();
v2.push(1);
v2.push(2);
// 6. 遍历
// (1) 不可变的遍历,只读
for i in &v2 {
println!("i = {}", i);
}
// (2) 可变的遍历,修改
for i in &mut v2 {
*i += 1; // 解引用
println!("i = {}", i);
}
// 7. 存储枚举类型值
enum Context {
Text(String),
Float(f32),
Int(i32),
};
let c = vec![
Context::Text(String::from(hello)),
Context::Int(1),
Context::Float(0.001),
];
// 8. 补充
let mut v = vec![1, 2, 3, 4];
let first = &v[0]; // 不可变引用
v.push(5); // 这里使用了v的可变引用,将导致之前的不可变引用不能再使用
// println!("first = {}", first);
}
// 1. 创建空的字符串
let mut s0 = String::new();
s0.push_str("hello");
println!("s0 = {}", s0);
// 2. 通过字面值创建字符串
let s1 = String::from("hello");
println!("s1 = {}", s1);
let s1 = "hello".to_string();
println!("s1 = {}", s1);
// 3. 更新字符串
// push_str()字面值
let mut s2 = String::from("hello");
s2.push_str(" world");
println!("s2 = {}", s2);
// push_str()String
let ss = " !".to_string();
s2.push_str(&ss); // 没有拿走ss的所有权,后面仍然可以使用ss
println!("s2 = {}", s2);
println!("ss = {}", ss);
// 使用push(),添加字符(rust中字符是32位)
let mut s2 = String::from("tea");
s2.push('m');
println!("s2 = {}", s2);
// 拼接两个字符串
let s1 = "hello".to_string();
let s2 = String::from(" world");
let s3 = s1 + &s2;
println!("s3 = {}", s3);
// println!("s1 = {}", s1); // 不能使用s1,因为s1的所有权给了s3(相当于把s2拼接到s1上,并把s1的所有权给s3)
println!("s2 = {}", s2); // 可以使用s2
// 使用format!宏拼接字符串
let s341 = String::from("tic");
let s342 = String::from("tac");
let s343 = String::from("toe");
let s344 = format!("{}-{}-{}", s341, s342, s343);
println!("s344 = {}", s344); // 输出tic-tac-toe
println!("s341 = {}", s341); // 仍然可以使用s341/s342/s343;
// 4. String不支持索引,因为rust中String使用的是utf-8,索引不一定能取到某个字符边界,比如中文;
let s4 = String::from("hello");
println!("h4.len = {}", s4.len()); // 输出5;
let s4 = String::from("你好");
println!("h4.len = {}", s4.len()); // 输出6;
// let res = s4[0]; // String不能被索引
// 5. str索引
let s5 = "你好";
let h5 = &s5[0..3]; // 取slice;
println!("h5 = {}", h5); // 输出“你”
// let h6 = &s5[0..2]; // 2不是字符边界,不能编译通过
// 6. 遍历
// 使用chars,取出的是完整的utf-8字符,比如一个完整的汉字;
for c in s4.chars() {
println!("c = {}", c); // 依次打印“你”和"好";
}
// bytes
for b in s4.bytes() {
println!("b = {}", b); // 打印出六个字节,b=228等
}
// 1. HashMap: 存储kv数据
// 2. 创建HashMap
use std::collections::HashMap; // 需要先导入HashMap的包
fn main() {
let mut scores : HashMap<String, i32> = HashMap::new();
scores.insert(String::from("Blue"), 10); // 使用insert添加值
scores.insert(String::from("Red"), 20);
// 通过vectore创建HashMap
let keys = vec![String::from("Blue"), String::from("Red")];
let values = vec![10, 20];
let scores : HashMap<_, _> = keys.iter().zip(values.iter()).collect(); // _是占位符
}
// 3. 读取
// 读取2中的sores
let k = String::from("Blue");
if let Some(v) = scores.get(&k) { // get()返回的是option
println!("v = {}", 10); // v = 10
}
// 4. 遍历:会以任意的顺序遍历出来
for (key, value) in &scores {
println!("{}, {}", key, value);
}
// 5. 更新
let mut ss = HashMap::new();
// 使用insert直接插入;
ss.insert(String::from("one"), 1);
ss.insert(String::from("two"), 2);
ss.insert(String::from("one"), 3); // 会覆盖掉前面插入的;
// 键不存在的时候才插入
let mut ss1 = HashMap::new();
ss1.insert(String::from("one"), 1);
ss1.insert(String::from("two"), 2);
ss1.entry((String::from("one")).or_insert(3); // entry判断是否存在,or_insert插入
println!("ss1 = {:?}", ss1);
// 根据旧值来更新一个值
let text = "hello world wonderful world";
let mut m = HashMap::new(); // 用于存储text中单词的计数
for word in text.split_whitespace() { // split_whitespace()使用空格分割
let count = map.entry(word).or_insert(0); // count是value的指针
*count += 1; // 更新value
}
println!("m = {:?}", m);
包、crate、模块等定义见https://www.rust-lang.org/zh-CN/learn文档;
// 创建模块
// 模块:用于控制作用域和私有性,默认是私有的;
mod factory { // mod关键字定义一个模块
pub mod produce_refrigerator { // pub表示public
pub fn produce_re() {
println!("produce regrigerator!");
}
}
pub mod produce_washing_machine {
pub fn produce_washing_maching() {
println!("produce washing_maching!");
}
}
}
fn main() {
factory::produce_regrigerator::produce_re();
}
创建lib库:“cargo new --lib mylib”;src目录里面自动生成lib.rs;在mylib中实现的模块需要在lib.rs中声明;
在mylib中创建mylib文件夹,mylib文件夹中创建factory.rs,同时factory也是模块名
pub mod produce_refrigerator { // pub表示public
pub fn produce_re() {
println!("produce regrigerator!");
}
}
pub mod produce_washing_machine {
pub fn produce_washing_maching() {
println!("produce washing_maching!");
}
}
在mylib库下src/lib.rs中声明模块:模块名和factory.rs的文件名一样
pub mod factory;
依赖模块:需要修改Cargo.toml配置依赖的其他模块,修改如下(path的路径是当前目录是因为创建的mylib是在当前工程下创建的子工程):
[dependencies]
mylib = {path = "./mylib"}
使用模块:两种方式,绝对路径和使用use导入(推荐use导入时路径到前一级模块)
use mylib::factory::produce_refrigerator;
use mylib::factory::produce_refrigerator::produce_re; // 不推荐直接到最后一级,可能和其他模块重名
use mylib::factory::produce_refrigerator as A; // 使用as关键字,给path别名
use mylib::factory::*; // 导入所有模块,包括produce_refrigerator和produce_washing_machine
fn main() {
mylib::factory::produce_refrigerator::produce_re(); // 使用的绝对路径,可以不使用use;mylib是库名,factory和produce_refrigerator是父子模块,produce_re函数
produce_refrigerator::produce_re(); // 使用use相对路径
produce_re(); // 使用use mylib::factory::produce_refrigerator::produce_re,但是如果多个模块下有函数重名,将有问题;
A::produce_re();
}
使用外部的lib库:
在Cargo.toml中导入依赖:添加库的名字以及版本
[dependencies]
rust-crypto = "0.2"
使用库:直接cargo run下面程序,cargo会自动下载crypto模块
extern crate crypto; // 表示外部库
use crypto::digest::Digest; // 导入库中的Digest
use crypto::Sha3;
fn main() {
let mut hasher = Sha3::sha3_256();
hasher.input_str("hello world");
let result = hasher.result_str(); // 对hello world字符串求hash
println!("hash = {}", result);
}
fn main() {
panic!("crash here"); // 程序会直接crash
}
// 运行cargo run前面加上RUST_BACKTRACE=1,可以帮组打印错误信息(只要是非0数字就行),默认是0,
RUST_BACKTRACE=1 cargo run
// 定义
enum Result {
Ok(T),
Err(E),
}
使用Result
use std::fs::File;
fn main() {
let f = File::open("hello.txt"); // 返回的f就是result结构
let r = match f {
Ok(flie) => file,
Err(error) => panic!("error: {:?}", error);
}
let f = File::open("hello.txt").unwrap(); // 如果是Ok,则会返回file
let f = File::open("hello.txt").expect("Failed to open hello.txt");
}
传播错误
use std::io;
use std::io::Read;
use std::fs::File;
fn main() {
let r = read_username_from_file();
match r {
Ok(s) => println!("s = {}", s);
Err(e) => println!("err = {?:}", e);
};
}
fn read_username_from_file() -> Result<String, io::Error> {
let f = File::open("hello.txt");
let mut f = match f {
Ok(file) => flie,
Err(error) => return Err(error),
};
let f = File::open("hello.txt")?; // 可以使用问号代替上面的match,如果有错误,直接就返回了error;
let mut s = String::new();
match f.read_to_string(&mut s) { // match的返回值就是函数的返回值
Ok(_) => Ok(s), // Ok(s)作为返回值
Err(error) => Err(error),
}
}
使用rust自带的测试工具;
创建lib库时,会在lib.rs中自动生成单元测试模块:
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
运行测试:
cargo test
// 1. 结构体中泛型
#[derive(Debug)]
struct Point<T, U> {
x: T,
y: U,
}
// 2. 函数中泛型
fn largest<T: PartialOld + Copy> (list: &[T]) -> T {
let mut T large = list[0];
for &item in list.iter() {
if item > large {
large = item;
}
}
large
}
// 3. 方法中泛型
// 为上面point实现get方法
impl<T, U> Point<T, U> {
fn get_x(&self) -> &T {
&self.x
}
fn get_y(self) -> U {
self.y
}
}
// 为point实现create新point方法;输入为其他泛型类型的point
// V, W是输入other的泛型,输出泛型是self和other的结合:
impl<T, U> Point<T, U> {
fn create<V, W> (self, other: Point<V, W>) -> Point<T, W> {
Point {
x: self.x,
y: other.y
}
}
}
fn main() {
let num_list = vec![1, 2, 3, 4];
let max = largest(&num_list);
print!("largest num = {}", max);
let p1: Point<char, char> = Point{x:'a', y:'b'};
let p2 = Point{x: 1, y: 2};
let p3 = p1.create(p2);
println!("{:?}", p3);
println!("Hello, world!");
}
类似接口类,trait中函数有默认实现;
// 定义一个trait
pub trait GetInfo {
fn get_x(&self) -> &u32;
fn get_y(&self) ->u32;
}
// 定义一个struct
pub struct Point {
pub x: u32,
pub y: u32,
}
// 为Point实现GetInfo trait
impl GetInfo for Point {
fn get_x(&self) -> &u32 {
&self.x
}
fn get_y(&self) ->u32 {
&self.y
}
}
// trait作为参数
pub fn print_info_x(item: impl GetInfo) {
print!("{}", item.get_x());
}
// trait作为返回值,返回的Point实现了trait GetInfo
pub fn get_obj() -> impl GetInfo {
Point{
x: 2,
y: 3,
}
}
// trait_bound
// 写法一
// T必须实现GetInfo和GetName两个trait
pub fn print_info_x<T: GetInfo+GetName>(item: T) {
print!("{}", item.get_x());
}
// 写法二
pub fn print_info_x<T>(item: T) where T: GetInfo+GetName{
print!("{}", item.get_x());
}
// 注意:返回值为trait时,函数中不能返回两种类型,即使这两种类型都实现了该trait,类似于C++中返回值是基类指针,返回两种以上子类对象
// 为trait实现trait
// 为实现了GetInfo的类型实现GetName,有点像多重继承
impl<T: GetInfo> GetName for T {
......
}
// 以下编译不过,r(`a)的生命周期大于了引用对象x(`b)的生命周期
fn main() {
let r; // ----------------------------------------------`a
{ //
let x = 5; //-------------+---------`b
r = &x; //
} //-----------------------`b
println!("r = {}", r); //
} //-----------------------------------------------`a
// 返回值是入参的引用
// 需要对入参和返回值显示标明生命周期标识符,并不是所有入参都需要标明生命周期,看入参与返回值是否有关
// 需要先在longest后面声明引用标识符(`a),后面才能使用该生命周期
fn longest<`a>(x: &`a str, y: &`a str) -> &`a str {
if x.len() > y.len() {
x
} else {
y
}
}
// error: r的所有权属于这个函数(局部变量)
fn a_str<`a>(x: &`a str, y: &`a str) -> &`a str {
let r = String::from("abc");
r.as_str()
}
fn main() {
let s1 = String::from("abcde");
let s2 = String::from("ab");
let r = longest(s1.as_str(), s2.as_str());
println!("r = {}", r);
}
// 结构体中成员变量是引用,需要生命周期标识符
// 同样,需要先在A后面声明生命周期标识符,标明引用类型的成员name的生命周期
#[derive(Debug)]
struct A<`a> {
name: &`a str,
}
fn main() {
let n = String::from("hello");
let a = A{name: &n};
println!("a = {:#?}", a);
}
遵守生命周期省略规则的情况下能明确变量的生命周期,则无需显示指定生命周期;函数或者方法的参数的生命周期成为输入生命周期,返回值的生命周期成为输出生命周期。
生命周期的注解省略规则适用于fn以及impl块定义,三条可以自动推断生命周期的规则如下:
// 只有一个输入,那么输入的生命周期被赋予了输出
fn get_s_str(s: &str) -> &str {
s
}
fn main() {
let s = String::from("hello");
let ss = get_s_str(s.as_str());
println!("ss = {}", ss);
}
struct StuA<`a> {
name: &`a str,
}
// impl后面的`a是声明一个声明周期,随后在StuA后面使用改生命周期标注
impl<`a> StuA<`a> {
fn do_something(&self) -> i32 {
3
}
// 等价于:fn do_something2<`a>(&`b self, s: &str) -> &`a str
fn do_something2(&self, s: &str) -> &str {
self.name
}
// 因为返回值是s,因此需要显式标明s的生命周期,否则编译提示返回值和输入不匹配
fn do_something3<`b>(&self, s: &`b str) -> &`b str {
s
}
}
fn main() {
let s = String::from("hello");
let a = StuA{name: &s};
println!("{}", a.do_something());
let s2 = String::from("hello");
println!("{}", a.do_something2(&s2)); // 返回的是a的name
}
定义方式:'static
静态生命周期存活于整个程序期间;
let s: &'static str = "hello";
闭包是可以保持进变量或者作为参数传递给其他函数的匿名函数(C++中lambda表达式)。闭包和函数的不同的是:1)闭包允许捕获调用者作用域中的值;2)闭包的定义可以省略参数和返回值的类型,编译器可以自动推导类型,但是只能自动推导一次;
// 函数格式:
fn add_one_v1(x: u32) -> u32 {
x + 1;
}
fn main() {
// 1. 多种定义闭包格式
// 与函数add_one_v1等价的闭包格式:
let add_one_v2 = |x: u32| -> u32 {x + 1;};
// 可以为每个参数和返回值自动推导一个具体的类型,但不能推导两次
let add_one_v3 = |x| {x + 1};
// 简单的函数体可以不用加花括号
let add_one_v4 = |x| x + 1;
// 2. 调用函数
let a = add_one_v1(5);
// 调用闭包
let b = add_one_v2(5);
let c = add_one_v3(5);
let d = add_one_v4(5);
// 错误使用:不能推导类型两次的例子,和模板还是有区别的
let example_closure = |x| x;
let s = example_closure(String::from("hello"));
println!("s = {}", s);
// 错误,第一次已经推导类型为String了,因此入参不能为5
let n = example_closure(5);
// 正确,多次使用是同样类型
let n = example_closure(5.to_string());
}
闭包可以通过三种方式捕获起环境,对应着函数的三种获取参数的方式,分别是获取所有权、可变借用和不可变借用;
这三种捕获方式被编码为如下三个Fn Trait:
1) FnOnce,捕获环境中变量,并获取其所有权;名称中Once部分代表了闭包不能多次获取相同变量的所有权(即该闭包不能多次调用);
2) FnMut,获取可变的借用值,可以改变环境中变量;当匿名函数体里改变了环境变量的值的时候,匿名函数就是FnMut;
3)Fn,从环境中获取不可变借用
定义闭包后,编译器会根据使用环境变量的方式来自动的推导闭包实现了哪些Fn Trait,所有闭包都实现了FnOnce,因为所有闭包都至少可以被调用一次;如对于下面例子中的闭包|x| x+i,则自动实现了不可变借用Fn;也可以显示的使用move来指定闭包获取环境变量的所有权(在使用move定义闭包时,如果变量具有copy语义,则变量执行拷贝,如果变量是move语义,则转移所有权);
fn main() {
let i = 1;
// 定义闭包,并捕获闭包环境中变量i
let exe = |x| x + i;
// 调用闭包
let r = exe(5);
println!("r = {}", r)
// 使用move定义闭包获取所有权;
let x = vec![1, 2, 3]; // vector是move语义
let equal_to_x = move |z| z==x; // 闭包定义后x的所有权就转移了;
println!("{?:}", x); // 不能编译通过,因为x所有权已经转移了;
assert!(equal_to_x(y));
}
闭包可以作为Fn被存储起来,例如作为回调函数,类似于C++中function模板
// 泛型T必须实现Fn trait,T可以作为函数调用
struct Cacher<T>
where T: Fn(u32) -> u32 {
calcuation: T, // 可以作为回调函数调用
value: u32,
}
impl<T> Cacher<T>
where T: Fn(u32) -> u32 {
fn new(calcu: T, i: u32) -> Cacher<T> {
Cacher {
calcu, // 结构体省略变量名的初始化方法
value: i,
}
}
fn Run(&self) -> u32 {
calcuation(value) // 调用回调函数
}
}
fn main() {
let cacher = Cacher::new(|x| x+1);
let x = cacher.Run();
println!("x = {}", x);
}
next是Iterator被要求实现的唯一的方法(即所有的迭代器都必须实现next方法),next一次返回一个元素,当迭代器结束时,返回None;
标准库迭代器中实现了多种方法,可以分为如下几种:
trait Iterator {
type Item; // trait关联类型,表示实现改trait的结构体需要指定该Item类型
fn next(&mut self) -> Option<Self::Item>; // type Item和self::Item这种用法叫做定义trait的关联类型
}
fn main() {
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter(); // for循环无需let mut,因为for循环获取了v1_iter的所有权,并将其置为可变
for val in v1_iter {
println!("val = {}", val)
}
let mut v1_iter = v1.iter(); // next方法定义中是mut self,所以需要let mut
if let Some(v) = v1_iter.next() {
println!("v = {}", v);
} else {
println!("At end");
}
// -------迭代可变引用-------
let mut v2 = vec![1, 2, 3];
let mut v2_iter = v2.iter_mut(); // v2.iter_mut(),迭代可变引用
if let Some(v) = v2_iter.next() {
*v = 3; // 加*解引用,对引用对象进行赋值
}
println!("v2 = {?:}", v2); // 打印3,2,3
// --------消费适配器--------
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
let total: i32 = v1_iter.sum(); // 调用消费适配器sum来求和
// --------迭代器适配器---------
let v1 = vec![1, 2, 3];
// v1.iter().map(|x| x+1);创建出一个新的迭代器,它并不会做任何事,因为迭代器是惰性的,除非调用了消费适配器;
let v2: Vec<> = v1.iter().map(|x| x + 1).collect(); // map: 对每一个元素执行闭包
println!("v2 = {?:}", v2);
let v3: Vec<> = v1.into_iter().filter(|x| *x > 5).collect();
println!("v3 = {?:}", v3);
}
struct Counter {
count: u32
}
impl Counter {
fn new() -> Counter {
Counter {count: 0}
}
}
impl Iterator for Counter {
type Item = u32; // 指定关联类型
fn next(&mut self) -> Option<Self::Item> {
self.count += 1;
if self.count < 6 {
Some(self.count)
} else {
None
}
}
}
fn main() {
let mut counter = Counter::new();
for i in (0..6) {
if let Some(v) = counter.next() {
println!("i = {}, v = {}", i, v);
} else {
println!("i = {}, at end", i);
break;
}
}
}
cargo run
:编译并运行,会生成target目录,里面包含debug版本的bin文件
cargo run --release
:编译并运行,生成的release版本
cargo build
:编译,也会生成target目录,生成debug版本
cargo build --release
:编译生成release版本的bin文件
配置优化级别(配置Cargo.toml文件),分别配置debug和release版本的优化级别;下列配置为默认的优化级别,不配置也可以
[profile.dev]
opt-level = 0
[profile.release]
opt-level = 3
[dependencies]
rust-crypto = "0.2"
use crypto::digest::Digest;
use crypto::sha3::Sha3;
fn main() {
let mut hasher = Sha3::sha3_256();
hasher.input_str("Hello World");
let result = hasher.result_str();
println!("result : {}", result);
}
cargo run
:此时会自动从crates.io导入crypto依赖的包,有点像java maven中配置依赖后自动导入依赖包
///
、//!
进行文档注释,///
对函数进行文档注释,//!
对crate进行文档注释,可以多行注释,支持Markdown语法cargo doc
,会在target文件夹中生成doc文件夹cargo doc --open
,生成doc并在浏览器中打开一个网页,显示自动生成的文档/// 给一个数加一
/// #Example
/// .......
pub fn add_one(x: i32) -> i32 {
x + 1
}
cargo login *****
来登录[package]
name = "package name"
version = "0.1.0"
license = "MIT" # 可以在Linux基金会的SPDX查看可以使用的标识
authors = ["zhangsan"]
description = "test crate"
cargo publish
进行发布cargo yank --vers 0.1.0
,撤回0.1.0版本为project或者lib设定工作空间,在构建大型工程时会经常使用;
新建Cargo.toml文件,并为adder和add-one设置当前目录为工作空间:
[workspace]
members = [
"adder",
"add-one",
]
可以在当前目录直接运行cargo new
/ cargo build
等命令来操作响应项目;
当前工作空间有多个项目,指定运行的项目:cargo run -p adder
有点类似c++ unique_ptr
fn main() {
let b = Box::new(5);
println!("b = {}", b);
}
使用Box
/*
// 定义一个链表List:这个链表是连续存储的,编译报错,因为List的类型大小不确定,在栈上定义变量大小需要确定;
enum List {
Cons(i32, List), // Cons是List的一个变量,它是一个匿名结构体,包含两个元素,第一个是i32,第二个是List
Nil,
}
// 等价的C++中定义:
struct List { // 栈内存大小不固定,编译报错
int value,
struct List l,
}
*/
// 使用Box定义List,编译通过,因为Box在栈上只占用一个指针的内存,是确定的
// 将Box当做一个指针来使用
enum List {
Cons(i32, Box<List>),
Nil,
}
fn main() {
use List::Cons;
use List::Nil;
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}
实现Deref trait允许我们重载解引用运算符;
/*
let a: A = A::new(); // 可以使用解引用的前提是类型A实现了Deref trait
let b = &a; // b为a的引用
let c = *b; // 解引用
*/
fn main() {
let x = 5;
let y = &5;
assert_eq!(5, x);
assert_eq!(5, *y);
let z = Box::new(x);
assert_eq!(5, *z);
}
例子:实现MyBox,并实现Deref trait
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T>{
MyBox(x)
}
}
// 注意:实际调用*MyBox的时候,返回值的类型是T
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
fn main() {
let x = 5;
let y = MyBox::new(x);
assert_eq!(5, x);
assert_eq!(5, *y);
}
解引用多态与可变性交互:T解引用为U,当传入&T时但是参数是&U时,自动将&T变为&U;类似&&T变为&T的感觉,但是&T不能自动解引用为T;
fn main() {
let m = MyBox::new(String::from("Rust"));
hello(&m); // 将MyBox变为&String,再将String的解引用变为字符串slice
}
fn hello(name: &str) {
println!("Hello, {}", name);
}
数据结构离开作用域执行,类似析构函数;
rust提供了std::mem::drop来提前释放数据结构;
struct Dog {
name: String,
count: i32,
}
impl Drop for Dog {
fn drop (&mut self) { // 必须时mut,因为资源释放需要修改对象
println!("{} leave", self.name);
// self.count -= 1;
}
}
fn main () {
let a = Dog{name: String::from("wangcai")};
{
let b = Dog{name: String::from("dahuang")};
println!("befor b leave");
}
// 提前释放a对象;
// a.drop(); drop方法不能显示的调用;
drop(a); // 调用std::mem::drop提前析构对象a;
println!("hello world");
}
引用计数智能指针,类似C++ shared_ptr;
通过Rc共享的数据只允许程序只读的共享;
// 实现可共享的链表
enum List {
// Cons(i32, Box)
Cons(i32, Rc<List>), // 使用Rc,因为Box不能共享
Nil,
}
use crate::List::{Cons, Nil};
use std::rc::Rc;
fn main() {
let a = Cons(1, Rc::new(Cons(2, Rc::new(Cons(3, Rc::new(Nil)))))); // a中所有节点都是Rc对象(智能指针)
// let b = Cons(2, Box::new(a)); // 如果是Box,会执行copy或者move,List会执行move从而转移了所有权,在c构造时就会编译错误;
let b = Cons(2, Rc::clone(&a)); // 将a拷贝一份,相当于将a中所有Rc指针节点拷贝一份,但是b和a会共用同一份堆中数据;当然,b比a会对一个节点,值为2;
// let b = Cons(2. a.clone()); 这种写法也可以,应该是自动为Cons对象添加了clone方法;
let c = Cons(3, Rc::clone(&a));
}
// 实现可共享的链表
#[derive(Debug)]
enum List {
Cons(Rc<RefCell<i32>>, Rc<List>),
Nil,
}
use crate::List::{Cons, Nil};
use std::rc::Rc;
use std::cell::RefCell;
fn main() {
let value = Rc::new(RefCell::new(5));
let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));
let b = Cons(Rc::new(RefCell::new(6)), Rc::clone(&a));
let c = Cons(Rc::new(RefCell::new(7)), Rc::clone(&a));
println!("a before : {:?}", a);
println!("b before : {:?}", b);
println!("b before : {:?}", b);
// 修改不可变value中的值;
*value.borrow_mut() += 10;
println!("a after : {:?}", a);
println!("b after : {:?}", b);
println!("c after : {:?}", c);
}
使用Rc引用,a引用了b, b引用了a,在打印时就会无限打印;可以使用上面的List作为例子,打印一个循环引用最后会栈溢出;
弱引用Weak
#[derive(Debug)]
enum List {
Cons(i32, RefCell<Weak<List>>),
Nil,
}
impl List {
fn tail(&self) -> Option<&RefCell<Weak<List>>> {
match self {
Cons(_, item) => Some(item),
Nil => None,
}
}
}
use crate::List::{Cons, Nil};
use std::rc::Rc;
use std::cell::RefCell;
use std::rc::Weak;
fn main() {
// 创建两个节点
let a = Rc::new(Cons(5, RefCell::new(Weak::new())));
let a = Rc::new(Cons(6, RefCell::new(Weak::new())));
// b指向a
if let Some(link) = b.tail() {
*link.borrow_mut() = Rc::downgrade(&a);
}
// a指向b
if let Some(link) = a.tail() {
*link.borrow_mut() = Rc::downgrade(&b);
}
println!("a tail:{}", a.tail());
}
use std::thread;
fn main() {
let v = vec![1, 2, 3];
let handle = thread::spawn(move ||
{println!("v:{:?}", v);
});
handle.join().unwrap();
}
使用std::thread创建线程,可以传入闭包或者函数;
rust提供1::1线程模型,即一个rust线程对应一个OS线程;
use std::thread;
// 主线程中启动子线程,并等待子线程结束;
fn main() {
let th = thread::spawn(|| {
for i in 1..10 {
println!("number in spawn {}", i);
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..5 {
println!("number in main {}", i);
thread::sleep(Duration::from_millis(1));
}
th.join().unwrap();
}
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(); // 发送消息;
});
let r_val = rx.recv().unwrap(); // 主线程接收数据;recv时阻塞的,所以不同join子线程;
println!("Got {}", r_val);
}
use std::thread;
use std::sync::mpsc;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel();
let tx1 = mpsc::Sender::clone(&tx);
thread::spawn(move || {
let val = String::from("th");
tx.send(val).unwrap();
});
thread::spawn(move || {
let val = String::from("th1");
tx1.send(val).unwrap();
});
// 主线程接收数据
for rec in rx {
println!("recevie {}", rec);
}
}
通道类似于单所有权,当值通过通道传递后,发送者无法使用这个值;
共享内存类似于多所有权,多个线程可以访问同一内存位置;
互斥器:Mutex,任意时刻只能有一个线程访问数据;互斥器使用时,需要先获得锁,使用完成后,需要释放锁;
Mutex是一个只能指针,lock方法返回一个MutexGuard的智能指针,内部提供drop方法,当MutexGuard离开作用域时自动释放锁;
use std::sync::Mutex;
fn main() {
let m = Mutex::new(5); // Mutex
{
let mut num = m.lock().unwrap(); // 获取锁,并获取互斥器中的值;
*num = 6; // 修改值
} // 锁离开作用域时自动释放
println!("m = {:?}", m);
}
多线程使用互斥器,使用Arc包裹Mutex;
Arc,线程安全的共享指针,Rc不是线程安全的,要将对象传递给另一个线程,需要使用Arc
use std::sync::Mutex;
use std::thread;
use std::sync::Arc;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let cnt = Arc::clone(&counter);
let h = thread::spawn(move || {
let mut num = cnt.lock().unwrap();
*num += 1;
});
handles.push(h);
}
for h in handles {
h.join().unwrap();
}
println!("counter is {}", *counter.lock().unwrap());
}
RefCell \ Rc与Mutex \ Arc
对象、封装、继承,对象由数据和操作数据的方法组成;
Rust中实现对象:结构体、枚举类型加上impl块就组成的对象;
Rust中没有继承的概念,rust可以同trait进行行为的共享(类似于继承);
封装就是定义结构体并添加相应impl块及方法;
创建lib、编写toml文件等;创建工程的过程比较多,这里不做介绍,前面的文档中有过程,具体流程需要做大型项目的时候再说;
实现一个库并定义struct(toml等的操作忽略);
dyn是rust关键字,表示是定义了某个trait的对象,及特征对象(类似于规定模板对象中规定实现模型trait
dyn为动态分发的意思:通过指针类型动态确定对象;(类似于C++中虚指针)
pub trait Draw {
fn draw(&self);
}
// pub struct Screen {
// pub component: Vec;
// }
pub struct Screen {
// dyn Draw表示Box中对象必须实现Draw trait
pub component: Vec<Box<dyn Draw>>,
}
pub struct SelectBox {
pub wight: i32,
pub hight: i32,
pub label: String,
}
impl Draw for SelectBox {
pub fn draw(&self) {
...
}
}
fn main() {
// dyn定义的对象需要动态分发
let s = Screen {
component: vec![
Box::new SelectBox {
wight: 1,
hight: 2,
label: String::from("abc"),
},
],
}
}
使用trait对象,要求对象必须是安全的;只用对象安全的trait,才可以组成trait对象,trait安全的对象需要满足下面两个条件:
//1、模式是Rust中特殊的语法,模式用来匹配值的结构。
//
//2、模式由如下内容组成:
//(1)字面值
//(2)解构的数组、枚举、结构体或者元组
//(3)变量
//(4)通配符
//(5)占位符
//1、match
//match VALUE {
// PATTERN => EXPRESSION,
// PATTERN => EXPRESSION,
// PATTERN => EXPRESSION,
//}
//必须匹配完所有的情况
//fn main() {
// let a = 1;
// match a {
// 0 => println!("Zero"),
// 1 => println!("One"),
// _ => println!("other"),
// };
// println!("Hello, world!");
//}
//if let
//fn main() {
// let color: Option<&str> = None;
// let is_ok = true;
// let age: Result = "33".parse();
//
// if let Some(c) = color {
// println!("color: {}", c);
// } else if is_ok {
// println!("is ok");
// } else if let Ok(a) = age {
// if a > 30 {
// println!("oh, mature man");
// } else {
// println!("oh, young man");
// }
// } else {
// println!("in else");
// }
//}
//while let
//只要模式匹配就一直执行while循环
//fn main() {
// let mut stack = Vec::new();
// stack.push(1);
// stack.push(2);
// stack.push(3);
//
// while let Some(top) = stack.pop() {
// println!("top = {}", top);
// }//只要匹配Some(value),就会一直循环
//}
//for
在for循环中,模式是直接跟随for关键字的值,例如 for x in y,x就是对应的模式
//fn main() {
// let v = vec!['a', 'b', 'c'];
// for (index, value) in v.iter().enumerate() {
// println!("index: {}, value: {}", index, value);
// }
//}
//此处的模式是(index, value)
//let
//let PATTERN = EXPRESSION
//fn main() {
// let (x, y, z) = (1, 2, 3); //(1, 2, 3)会匹配(x, y, z),将1绑定到x,2绑定到y,3绑定到z
// println!("{}, {}, {}", x, y, z);
//
// let (x, .., z) = (1, 2, 3);
// println!("{}, {}", x, z);
//}
//函数
//函数的参数也是模式
fn print_point(&(x, y): &(i32, i32)) {
println!("x: {}, y: {}", x, y);
}
fn main() {
let p = (3, 5);
print_point(&p);
}
//&(3, 5) 匹配模式 &(x, y)
//模式在使用它的地方并不都是相同的,模式存在不可反驳的和可反驳的
模式有两种,可反驳(refutable)的和不可反驳(irrefutable),能匹配任何传递的可能值的模式称为不可反驳的(如match);对值的匹配可能失败的模式称为可反驳的(如if let);
fn main() {
let x = 1;
match x {
1 => println!("1"),
2 => println!("2"),
_ => println!("xx");
}
}
fn main() {
let x = Some(5); // x是一个option
let y = 10; //位置1
match x {
Some(50) => println!("50"), // 值为50的some匹配这个
Some(y) => println!("value = {}", y), //此处的y不是位置1的y,所有非50的some都会匹配这个
_ => println!("other"),
};
println!("x = {:?}, y = {:?}", x, y); //此处的y是位置1的y
}
fn main() {
let x = 1;
match x {
1|2 => println!("1 or 2"), //|表示是或,匹配1或者2
3 => println!("3"),
_ => println!("xx"),
};
}
fn main() {
// 对数字进行匹配
let x = 5;
match x {
1..=5 => println!("1 to 5"), // 1|2|3|4|5 匹配1到5包括5;
_ => println!("xx"),
};
// 对字符进行匹配
let y = 'c';
match y {
'a'..='j' => println!("1"),
'k'..='z' => println!("2"),
_ => println!("other"),
}
}
struct Point {
x: i32,
y: i32,
}
fn main() {
/ / 写法1,let
let p = Point{x: 1, y: 2};
let Point{x: a, y: b} = p; // 解构p,将x的值放在a里,将y值放在b里;
assert_eq!(1, a);
assert_eq!(2, b);
// 写法2,let
let Point{x, y} = p; // 类似于写法 let Point{x: x, y: y} = p;
assert_eq!(1, x);
assert_eq!(2, y);
// 写法3,使用match匹配,对结构体部分值进行解构
let p = Point{x: 1, y: 0};
match p {
Point{x: 1, y: 0} => println!("matched"), // 同时解构x和y
Point{x, y: 0} => println!("x axis"), // 解构y
Point{x: 0, y} => println!("y axis"), // 解构x
Point{x, y} => println!("other"),
};
}
解构枚举类型
enum Message {
Quit,
Move{x: i32, y: i32},
Write(String),
ChangeColor(i32, i32, i32),
}
fn main() {
let msg = Message::ChangeColor(0, 160, 255);
match msg {
Message::Quit => {
println!("quit");
},
Message::Move{x, y} => {
println!("move, x: {}, y: {}", x, y);
},
Message::Write(text) => println!("write msg: {}", text),
Message::ChangeColor(r, g, b) => {
println!("clolor, r: {}, g: {}, b: {}", r, g, b);
},
};
}
----------------------------------------------------------------------------
// 嵌套枚举类型解构匹配
enum Color {
Rgb(i32, i32, i32),
Hsv(i32, i32, i32),
}
enum Message {
Quit,
Move{x: i32, y: i32},
Write(String),
ChangeColor(Color),
}
fn main() {
let msg = Message::ChangeColor(Color::Hsv(0, 160, 255));
match msg {
Message::ChangeColor(Color::Rgb(r, g, b)) => {
println!("rgb color, r: {}, g: {}, b: {}", r, g, b);
}, // 匹配ChangeColor中的Rgb
Message::ChangeColor(Color::Hsv(h, s, v)) => {
println!("hsv color, h: {}, s: {}, v: {}", h, s, v);
},
_ => ()
};
}
解构结构体和元组的混合
struct Point{
x: i32,
y: i32,
}
fn main() {
let ((a, b), Point{x, y}) = ((1, 2), Point{x: 3, y: 4}); // 元组中包含子元组和结构体
println!("a: {}, b: {}, x: {}, y: {}", a, b, x, y);
}
fn foo(_: i32, y: i32) {
println!("y = {}", y);
}
//trait A {
// fn bar(x: i32, y: i32);
//}
//
//struct B {}
//
//impl A for B {
// fn bar(_: i32, y: i32) { // 忽略函数中第一个入参
// println!("y = {}", y);
// }
//}
fn main() {
foo(1, 2);
let numbers = (1, 2, 3, 4);
match numbers {
(one, _, three, _) => { // 使用_忽略元组中部分值
println!("one: {}, three: {}", one, three);
},
}
}
// 变量名加_前缀,表示编译器忽略该变量;
fn main() {
let _x = 5; // 忽略此变量;
let _y = 5; // 忽略此变量;
//let s = Some(String::from("hello"));
//if let Some(_c) = s {
if let Some(c) = s {
// println!("found a string");
//}
println!("s: {:?}", s);
let s = Some(String::from("hello"));
if let Some(_) = s {
println!("found a string");
}
println!("s: {:?}", s);
}
fn main() {
let num = Some(4);
let y = 10; //位置1
match num {
Some(x) if x == y => println!("num == y"),//此处的y是位置1处的y
Some(x) => println!("x: {}", x),
None => (),
};
let x = 4;
let y = false;
match x {
4|5|6 if y => println!("1"),//4|5|6 if y a: 4|5|(6 if y) b: ((4|5|6) if y)(等价于此种)
_ => println!("2"),
}
}
enum Message {
Hello{id: i32},
}
fn main() {
let msg = Message::Hello{id: 25};
match msg {
Message::Hello{id: id_va @ 3..=7} => { // 创建变量id_va,并匹配3到7
println!("id_va: {}", id_va);
},
Message::Hello{id: 10..=20} => { // 匹配10到20
println!("large");
},
Message::Hello{id} => { // 同名变量可以忽略
println!("id: {}", id);
},
}
println!("Hello, world!");
}
1、在此节之前讨论过的都是安全的Rust,即Rust在编译时会强制执行的内存安全保证。不会强制执行这类内存安全保证的,就是不安全的Rust。
2、不安全的Rust存在的两大原因:
(1)静态分析本质上是保守的,就意味着某些代码可能是合法的,但是Rust也会拒绝。在此情况下,可以使用不安全的代码。
(2)底层计算机硬件固有的不安全性。如果Rust不允许进行不安全的操作,有些任务根本就完成不了。
3、不安全的Rust具有的超级力量
Rust会通过unsafe关键字切换到不安全的Rust。不安全的Rust具有以下超级力量:
(1)解引用裸指针
(2)调用不安全的函数或者方法
(3)访问或修改可变静态变量
(4)实现不安全的trait
注意:unsafe并不会关闭借用检查器或禁用任何其它的Rust安全检查规则,它只提供上述几个不被编译器检查内存安全的功能。unsafe也不意味着块中的代码一定就是不ok的,它只是表示由程序员
// 解引用裸指针
// 不可变和可变的,分别写作*const T, *mut T
//
// (1)允许忽略借用规则,可以同时拥有不可变和可变的指针,或者是多个指向相同位置的可变指针
// (2)不保证指向的内存是有效的
// (3)允许为空
// (4)不能实现任何自动清理的功能
fn main() {
let mut num = 5;
//创建不可变和可变的裸指针可以在安全的代码中,只是不能在不安全代码块之外解引用裸指针
let r1 = &num as *const i32; // 使用as进行类型转换,将引用转换为裸指针
let r2 = &mut num as *mut i32;
unsafe {
println!("r1 is: {}", *r1); // 使用裸指针才需要unsafe包起来
println!("r2 is: {}", *r2);
}
let add = 0x12345usize;
let _r = add as *const i32;
}
// 调用不安全的函数或者方法
unsafe fn dangerous() { // unsafe函数
println!("do something dangerous");
}
fn foo() {
let mut num = 5;
let r1 = &num as *const i32;
let r2 = &mut num as *mut i32;
unsafe {
println!("*r1 = {}", *r1);
println!("*r2 = {}", *r2);
}
}
fn main() {
unsafe {
dangerous();
}
//dangerous(); //error
foo();
println!("Hello, world!");
}
调用c语言的函数
extern "C" { // 声明c函数接口
fn abs(input: i32) -> i32;
}
fn main() {
unsafe {
println!("abs(-3): {}", abs(-3));
}
println!("Hello, world!");
}
c语音调用rust函数
[package]
name = "foo"
version = "0.1.0"
authors = ["anonymousGiga "]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[lib]
name = "foo"
crate-type = ["staticlib"]
rust lib编写
#![crate_type = "staticlib"]
#[no_mangle]
// 定义foo函数,以供c程序使用
// 不懂这个函数为什么会使用extern
pub extern fn foo() {
println!("use rust");
}
extern void foo(); // 声明rust接口
int main() {
foo();
return 0;
}
gcc -o main main.c libfoo.a -lpthread -ldl
静态变量和常量的区别:
1、静态变量有一个固定的内存地址(使用这个值总会访问相同的地址),常量则允许在任何被用到的时候复制其数据。
2、静态变量可以是可变的,虽然这可能是不安全(用unsafe包含)
//static HELLO_WORLD: &str = "Hello, world";
//
//fn main() {
// println!("{}", HELLO_WORLD);
//}
static mut COUNTER: u32 = 0;
fn add_counter(inc: u32) {
unsafe {
COUNTER += inc;
}
}
fn main() {
add_counter(3);
add_counter(3);
unsafe {
println!("counter: {}", COUNTER);
}
}
unsafe trait Foo {
fn foo(&self);
}
struct Bar();
unsafe impl Foo for Bar {
fn foo(&self) {
println!("foo");
}
}
fn main() {
let a = Bar();
a.foo();
println!("Hello, world!");
}
// 定义关联类型
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
// 定义与关联类型类似的模板
pub trait Iterator1<T> {
fn next(&mut self) -> Option<T>;
}
struct A {
value: i32,
}
impl Iterator1<i32> for A {
fn next(&mut self) -> Option<i32> {
println!("in i32");
if self.value > 3 {
self.value += 1;
Some(self.value)
} else {
None
}
}
}
impl Iterator1<String> for A {
fn next(&mut self) -> Option<String> {
println!("in string");
if self.value > 3 {
self.value += 1;
Some(String::from("hello"))
} else {
None
}
}
}
fn main() {
let mut a = A{value: 3};
//a.next(); // 如果A实现的是关联类型的trait,则可以直接调用
// 使用泛型实现的的struct调用trait中函数时必须标注类型,使用完全限定语法
<A as Iterator1<i32>>::next(&mut a); //完全限定语法,带上了具体的类型
<A as Iterator1<String>>::next(&mut a);
println!("Hello, world!");
}
use std::ops::Add;
#[derive(Debug, PartialEq)]
struct Point {
x: i32,
y: i32,
}
// 实现ops::Add trait,以重载+运算符
impl Add for Point {
type Output = Point;
fn add(self, other: Point) -> Point {
Point {
x: self.x + other.y,
y: self.y + other.y,
}
}
}
#[derive(Debug)]
struct Millimeters(u32);
struct Meters(u32);
impl Add<Meters> for Millimeters{
type Output = Millimeters;
fn add(self, other: Meters) -> Millimeters {
Millimeters(self.0 + other.0 * 1000)
}
}
fn main() {
assert_eq!(Point{x: 1, y: 1} + Point{x:2, y: 2}, Point{x: 3, y: 3});
let mi = Millimeters(1);
let m = Meters(1);
let r = mi + m;
println!("r = {:?}", r);
println!("Hello, world!");
}
//尖括号里面为泛型的默认类型参数,RHS是一个泛型类型参数(right hand side)
//trait Add { // 这里泛型RHS默认是Self类型
// type Output;
// fn add(self, rhs: RHS) -> Self::Output;
//}
struct实现了一个或多个trait,但是具有多个同名的方法,分为两种情况来区分调用这些方法;
trait A {
fn print(&self);
}
trait B {
fn print(&self);
}
struct MyType;
impl A for MyType {
fn print(&self) {
println!("A trait for MyType");
}
}
impl B for MyType {
fn print(&self) {
println!("B trait for MyType");
}
}
impl MyType {
fn print(&self) {
println!("MyType");
}
}
fn main() {
let my_type = MyType;
my_type.print(); //等价于MyType::print(&my_type);
A::print(&my_type); // 将my_type作为self参数传入
B::print(&my_type);
println!("Hello, world!");
}
trait Animal {
fn baby_name() -> String;
}
struct Dog;
impl Dog {
fn baby_name() -> String {
String::from("Spot")
}
}
impl Animal for Dog {
fn baby_name() -> String {
String::from("puppy")
}
}
fn main() {
println!("baby_name: {}", Dog::baby_name());
//println!("baby_name: {}", Animal::baby_name()); // 编译报错
println!("baby_name: {}", <Dog as Animal>::baby_name()); //完全限定语法
}
父 trait 用于在另一个 trait 中使用某 trait 的功能,有时我们可能会需要某个 trait 使用另一个 trait 的功能,在这种情况下,需要能够依赖相关的 trait 也被实现。这个所需的 trait 是我们实现的 trait 的 父(超) trait(supertrait)。
use std::fmt;
trait OutPrint: fmt::Display { // 要求实现Display trait
fn out_print(&self) {
let output = self.to_string();
println!("output: {}", output);
}
}
struct Point{
x: i32,
y: i32,
}
impl OutPrint for Point {}
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
println!("Hello, world!");
}
newtype 模式用以在外部类型上实现外部 trait;
孤儿规则(orphan rule):只要 trait 或类型对于当前 crate 是本地的话就可以在此类型上实现该 trait。
一个绕开这个限制的方法是使用 newtype 模式(newtype pattern)。
// 功能:为Vec实现Display trait,因为它们都不是在此crate中定义的,所以不能直接实现;因此我们定义一个Wrapper来包含Vec,再为Wrapper实现Display trait,从而间接的达到为Vec实现trait的功能;
use std::fmt;
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({})", self.0.join(","))
}
}
fn main() {
let w = Wrapper(vec![String::from("hello"), String::from("World")]);
println!("w = {}", w);
println!("Hello, world!");
}
使用type关键字为类型区别名,类似C++中using T = Type
type Kilometers = i32;
fn main() {
let x: i32 = 5;
let y: Kilometers = 6;
let r: i32 = x + y;
println!("x + y = {}", r);
}
Rust 有一个叫做 !
的特殊类型。在类型理论术语中,它被称为 empty type,因为它没有值。
我们更倾向于称之为 never type。在函数不返回的时候充当返回值
fn bar() -> ! {
loop{}
}
以下例子为《Rust程序设计语言》中第二章“猜猜看”游戏的例子,不能直接允许,需要在toml里面添加依赖;
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1, 101);
loop {
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = match guess.trim().parse() { // 这里进行了变量隐藏,后定义的guess隐藏了前面的同名变量
Ok(num) => num,
Err(_) => continue, //continue 的值是 !。
//当 Rust 要计算 guess 的类型时,它查看这两个分支。
//前者是 u32 值,而后者是 ! 值。
//因为 ! 并没有一个值,Rust 根据前面的match返回值判断决定 guess 的类型是 u32
};
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
}
}
函数指针允许我们使用函数作为另一个函数的参数。
函数的类型是 fn ,fn 被称为 函数指针。指定参数为函数指针的语法类似于闭包。
函数指针实现了Fn、FnMut、FnOnce
fn add_one(x: i32) -> i32 {
x + 1
}
fn do_twice(f: fn(i32) -> i32, val: i32) -> i32 {
f(val) + f(val)
}
fn wapper_func<T>(t: T, v: i32) -> i32
where T: Fn(i32) -> i32 {
t(v)
}
fn func(v: i32) -> i32 {
v + 1
}
fn main() {
let r = do_twice(add_one, 5);
println!("r = {}", r);
//+++++++++++++++++
let a = wapper_func(|x| x+1, 1);
println!("a = {}", a);
let b = wapper_func(func, 1);
println!("b = {}", b);
}
将闭包作为返回值: Fn没有实现size,需要用Box包起来
//fn return_clo() -> Fn(i32) -> i32 {
// |x| x+1
//}
fn return_clo() -> Box<dyn Fn(i32)->i32> {
Box::new(|x| x+1)
}
fn main() {
let c = return_clo();
println!("1 + 1 = {}", c(1));
println!("1 + 1 = {}", (*c)(1)); // 解引用多态
println!("Hello, world!");
}
Rust中的宏主要有两种,一种是使用macro_rules!的声明宏,一种是过程宏。而过程宏又主要分为三种:
(1)自定义宏#[derive],在结构体、枚举等上指定通过derive属性添加代码;
(2)类属性宏,定义可用于任意项的自定义属性;
(3)类函数宏,看起来像函数但是作用于作为参数传递的Token。
宏和函数
(1)宏是一种为写其它代码而写代码的方式。宏对于减少大量编写代码和维护代码非常有用。
(2)一个函数标签必须声明函数参数个数和类型,宏只接受可变参数。
(3)宏的定义比函数的定义更复杂。
(4)在调用宏 之前 必须定义并将其引入作用域,而函数则可以在任何地方定义和调用。
过程宏接收 Rust 代码作为输入,在这些代码上进行操作,然后产生另一些代码作为输出,而非像声明式宏那样匹配对应模式然后以另一部分代码替换当前代码。
类属性宏
类属性宏与自定义派生宏相似,不同于为 derive 属性生成代码,它们允许你创建新的属性。
例子:
可以创建一个名为 route 的属性用于注解 web 应用程序框架(web application framework)的函数:
#[route(GET, “/”)]
fn index() {
#[route] 属性将由框架本身定义为一个过程宏。其宏定义的函数签名看起来像这样:
#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
说明:类属性宏其它工作方式和自定义derive宏工作方式一致。
类函数宏
类函数宏定义看起来像函数调用的宏。类似于 macro_rules!,它们比函数更灵活。
例子:
如sql!宏,使用方式为:
let sql = sql!(SELECT * FROM posts WHERE id=1);
则其定义为:
#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
宏的资料推荐
https://danielkeep.github.io/tlborm/book/mbe-macro-rules.html
定义过程宏的函数接受一个 TokenStream 作为输入并产生一个 TokenStream 作为输出。这也就是宏的核心:宏所处理的源代码组成了输入 TokenStream,同时宏生成的代码是输出 TokenStream。
use proc_macro;
#[some_attribute]
pub fn some_name(input: TokenStream) -> TokenStream {
}
vec!
就是一个声明宏,这是一个模式匹配的过程,形式类似match其中*
表示任意数量,()
内是表达式
// 声明一个声明宏
#[macro_export] // 表明这个宏可以被导出
macro_rules! my_vec { //my_vec! 模仿vec!用于创建vector
($($x: expr), *) => {
{
let mut temp_vec = Vec::new();
$(
temp_vec.push($x);
)*
temp_vec
}
};
}
// 过程宏中的derive宏
#[derive(Debug)] // 自动为结构体实现了fmt::Display trait
struct A {
a : i32,
}
实现一个自定义宏,能够为结构体自动实现HelloMacro trait
pub trait HelloMacro {
fn hello_macro();
}
proc_macro_derive
定义HelloMacro
宏的实现,其实是实现一个输入输出都是TokenStream的fnextern crate proc_macro;
use crate::proc_macro::TokenStream;
use quote::quote;
use syn;
fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let gen = quote! {
impl HelloMacro for #name {
fn hello_macro() {
println!("Hello, in my macro, my name is {}", stringify!(#name));
}
}
};
gen.into()
}
// 输入和
#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap(); // 解析输入为DeriveInput
impl_hello_macro(&ast)
}
DeriveInput结构体是quote包中定义的,大致如下:
DeriveInput {
// --snip--
ident: Ident {
ident: "Pancakes",
span: #0 bytes(95..103)
},
data: Struct(
DataStruct {
struct_token: Struct,
fields: Unit,
semi_token: Some(
Semi
)
}
)
}
use hello_macro::HelloMacro;
use hello_macro_derive::HelloMacro;
#[derive(HelloMacro)]
struct Main;
fn main() {
Main::hello_macro(); // 调用HelloMacro的hello_macro方法
println!("Hello, world!");
}
Cargo.toml文件中添加依赖
[dependencies]
serde = "*"
serde_derive = "*"
serde_json = "*"
json库采用serde json, 文档:https://serde.rs/
extern crate serde_json;
use std::fs::File;
fn preparedata() -> String {
let f = File::open("/export/rust/test_json/sample.json").unwrap();
let values : serde_json::Value = serde_json::from_reader(f).unwrap();
serde_json::to_string(&values).unwrap()
}
fn main() {
let r = preparedata();
println!("r:{}", r);
}
extern crate serde_json;
use serde_json::{Value, json};
fn test_json() -> String {
let mut value = json!({});
value.as_object_mut().unwrap().insert("f1".to_string(), Value::String("v1".to_string()));
value.as_object_mut().unwrap().insert("f2".to_string(), Value::String("v2".to_string()));
serde_json::to_string(&value).unwrap()
}
fn main() {
let r = test_json();
println!("r:{}", r);
}
yum install -y protobuf
cargo install protobuf
,使用protoc编译proto文件为rs文件需要此插件cargo install protobuf-codegen-pure
,使用rust脚本编译proto文件为rs文件需要(也可以后面将protobuf-codegen-pure的依赖写入,cargo会自动下载);syntax="proto2";
message testpb {
optional int32 left = 1;
optional string right = 2;
}
fn main() {
protobuf_codegen_pure::Codegen::new()
.out_dir("src/protos")
.inputs(&["protos/test.proto"])
.include("protos")
.run()
.expect("Codegen failed.");
}
Cargo.toml文件中导入依赖,build.rs脚本需要此依赖包,这里选3.0.0-alpha.1是因为json转pb是在3.0版本后才有的功能,而编译proto文件使用的protobuf-codegen-pure和后面使用的protobuf需要版本一致,否则编译出错:[build-dependencies]
protobuf-codegen-pure = "3.0.0-alpha.1"
编译test.proto为test.rs,build.rs中设置的输出文件夹是src/protos,这个需要提前创建,编译命令:cargo build
,并为protos目录创建mod.rs,src目录创建lib.rspub mod test;
src/lib.rs内容为:pub mod protos;
最终的目录结构:.
├── build.rs
├── Cargo.toml
├── protos
│ └── test.proto
└── src
├── main.rs
├── lib.rs
└── protos
├── mod.rs
└── test.rs
[dependencies]
protobuf = "3.0.0-alpha.1"
main.rs中使用proto中定义的数据结构testpb:
use test_pb::protos::test::*;
fn main() {
let mut p = Testpb::new();
p.set_left(3);
p.set_right(String::from("hello"));
println!("p:{ :?}", p);
}
protobuf依赖需要3.0以上;
以上面的test.proto为例,编译出来Testpb的proto对象;
extern crate protobuf;
use serde_json::{Value, json};
use test_pb::protos::test::*;
fn test_pb() {
let mut p = Testpb::new();
p.set_left(3);
p.set_right(String::from("hello"));
println!("p:{:?}", p);
}
fn test_json_pb() {
// 构造json字符串
let mut value = json!({});
value.as_object_mut().unwrap().insert("left".to_string(), serde_json::from_str("3").unwrap());
value.as_object_mut().unwrap().insert("right".to_string(), Value::String("hello".to_string()));
let s = serde_json::to_string(&value).unwrap(); // json字符串
// json字符串转Testpb
// parse_from_str为泛型函数,本在protobuf::json::parse模块下,parse是私有模块,protobuf中使用了pub use来公开parse_from_str函数;
let p = protobuf::json::parse_from_str::<Testpb>(&s).unwrap();
println!("p:{:?}", p);
let left = p.get_left();
let right = p.get_right();
println!("{},{}", left, right);
}
fn main() {
test_json_pb();
}