柚子快报邀请码778899分享:开发语言 rust基础学习
安装
1.下载RUST
官网:https://www.rust-lang.org/zh-CN/tools/install
下载后直接点开一直输入1即可;
2.安装成功后
语法
1.输出到命令行
fn main() {
let a = 123;
println!("a = {}",a);
}
println!():换行输出print!():不换行输出
2.基础语法
2.1 let
rust是强类型语言,声明变量使用let关键字
let a = 123;
声明之后不可以改变变量的类型
a = "abc";
a = 4.56;
a = 456;
前两行错误时由于变量声明之后类型不可改变,第三行错误是因为声明变量后值不可改变
--> src\main.rs:2:9
|
2 | let a = 123;
| ^
|
= note: `#[warn(unused_assignments)]` on by default
= help: maybe it is overwritten before being read?
error[E0384]: cannot assign twice to immutable variable `a`
--> src\main.rs:3:5
|
2 | let a = 123;
| -
| |
| first assignment to `a`
| help: consider making this binding mutable: `mut a`
3 | a = 456;
| ^^^^^^^ cannot assign twice to immutable variable
2.2 mut(mutable)声明可变值变量
let mut a = 123;
a = 456;
变量和不可变变量的区别
let a = 123;//合法,可以编译,但可能有警告,因为变量没有被使用
let a = 456;
如果a是常量就不合法
const a: i32 = 123;
let a = 456;
报错
--> src\main.rs:3:9
|
2 | const a: i32 = 123;
| ------------------- constant defined here
3 | let a = 456;
| ^
| |
| interpreted as a constant pattern, not a new variable
| help: introduce a variable instead: `a_var`
|
= note: the matched value is of type `i32`
变量的值可以"重新绑定",但在"重新绑定"以前不能私自被改变,这样可以确保在每一次"绑定"之后的区域里编译器可以充分的推理程序逻辑。
2.3 重影(shadowing)
重影就是重新绑定。(与重载,重写不同)
let x = 5;
let x = x + 1;
let x = x * 2;
print!("The value of x is: {}", x);
运行结果:
The value of x is: 12
重影与可变变量赋值不同
重影:同一个名字重新代表另一个变量实体,其属性、可变属性和值都可以改变变量赋值:仅发生值得变化。
let mut s = "123";
s = s.len();
运行结果:
--> src\main.rs:3:9
|
2 | let mut s = "123";
| ----- expected due to this value
3 | s = s.len();
| ^^^^^^^ expected `&str`, found `usize`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `greeting` due to previous error
重影:
let s = "123";
let s = s.len();
print!("s = {}", s);
运行结果:
s = 3;
3.基本数据类型
3.1 整型(Integer)
位长度有符号无符号8-biti8u816-biti16u1632-biti32u3264-biti64u64128-biti128u128archisizeusize
3.2 浮点数(Float-point)
默认为64位,可声明为32位
let x = 2.0; //f64
let y: f32 = 3.0; //f32
3.3 布尔类型(bool)
true,false
3.4 字符类型(char)
Rust中char类型大小为4个字节
3.5 复合类型
元组用一对()包括的一组数据,可以包含不同种类的数据:
fn main() {
let tup = (500, 6.4, 1); // 类型可自动识别
let (x, y, z) = tup;
print!("x = {},y = {},z = {}",x,y,z);
}
结果:
x = 500,y = 6.4,z = 1
数组用一对[ ]包括的同类型数据
let a = [1,2,3,4,5];
let a: [i32; 5] = [1,2,3,4,5];
//数组访问
let a = [1,2,3,4,5];
let b = a[1];
print!("b = {}",b);
//数组变更
a[0] = 2; //错误:数组a不可变
let mut a = [1,2,3,4,5];
a[0] = 2;//正确
4.函数
4.1 函数格式
fn <函数名> ( <参数> ) <函数体>
rust函数命名是小写字母以下划线分割:
fn main() {
println!("hello,world");
another_function();
}
fn another_function() {
println!("hello,Rust");
}
运行结果:
hello,world
hello,Rust
4.2 带参函数
fn main() {
another_function(5, 5.6);
}
fn another_function(x: i32, y: f64) {
println!("x = {} , y = {}", x, y);
}
运行结果:
x = 5 , y = 5.6
4.3 函数体的语句和表达式
rust可以在{ }中编写一个较为复杂的表达式
fn main() {
let x = 5;
let y = {
let x = 3;
x + 1 //不带";"表明是表达整个块的值
};
println!("x = {} , y = {}", x, y);
}
4.4 函数返回值
使用"->“声明函数返回值的类型(不是用”
fn main() {
println!("33 + 4 = {}", add(33, 4));
}
fn add(x: i32, y: i32) -> i32 { //不能不写"-> i32",函数无法自动识别返回类型
return x + y;
}
结果:
33 + 4 = 37
如果不写返回类型则表示没有返回值,是一个纯过程
5.条件判断
if后的条件可以不加括号判断之后的函数体重即使只有一句话也必须加{ }条件值必须是bool类型
fn main() {
let number = 3;
if number < 5 {
println!("条件为 true");
} else {
println!("条件为 false");
}
}
对比java中的:
String a = 1 > 0 ? "ture" : "false";
在rust中可以表示为:
let a = if 1 > 0 { "ture" } else { "false" };
6.循环
6.1 while循环
rust中只有while循环,没有类似于 for (i = 0; i < 10; i++) 的循环,需要时可以使用while实现
fn main() {
let mut i = 1;
while i < 4 {
println!("{}", i);
i += 1;
}
}
运行结果:
1
2
3
6.2 for循环
使用迭代器
fn main() {
let a = [1, 2, 3, 4, 5];
for i in a.iter() {
println!("{}", i);
}
}
运行结果:
1
2
3
4
5
使用下标
fn main() {
let a = [1, 2, 3, 4, 5];
for i in 0..5 {
println!("{}", a[i]);
}
}
loop循环
fn main() {
let a = ['r', 'o', 'o', 'i', 'k', 'e'];
let mut i = 0;
loop {
if a[i] == 'k' {
break;
}
println!("{}", a[i]);
i += 1;
}
}
loop可以return返回值
fn main() {
let a = ['r', 'o', 'o', 'i', 'k', 'e'];
let mut i = 0;
let _index = loop {
if a[i] == 'k' {
break i;
}
println!("{}", a[i]);
i += 1;
};
println!("停止的下标是:{}", _index);
}
输出结果:
r
o
o
i
停止的下标是:4
7.所有权
7.1 规则
每个值都有一个变量,成为其所有者一次只能有一个所有者当所有者不在运行范围时,该值将被删除
7.2 变量范围
{
// 在声明以前,变量 s 无效
let s = "runoob";
// 这里是变量 s 的可用范围
}
// 变量范围已经结束,变量 s 无效
7.3 变量与数据交互的方式
移动(Move)
let x = 5;
let y = x;
堆栈中的字符串只能被赋值给一个对象,如先赋值给s1,再赋给s2,则s1无效
fn main() {
let s1 = String::from("hello");
let s2 = s1;
println!("{},world!", s1);
}
运行结果:
warning: unused variable: `s2`
--> src\main.rs:3:9
|
3 | let s2 = s1;
| ^^ help: if this is intentional, prefix it with an underscore: `_s2`
|
= note: `#[warn(unused_variables)]` on by default
error[E0382]: borrow of moved value: `s1`
--> src\main.rs:4:27
|
2 | let s1 = String::from("hello");
| -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
3 | let s2 = s1;
| -- value moved here
4 | println!("{},world!", s1);
| ^^ value borrowed here after move
|
= note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)
克隆(Clone)
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone();
println!("{},world!", s1);
println!("{},world!", s2);
}
运行结果:
hello,world!
hello,world!
这里是真的将堆中的 “hello” 复制了一份,所以 s1 和 s2 都分别绑定了一个值,释放的时候也会被当作两个资源。
当然,克隆仅在需要复制的情况下使用,毕竟复制数据会花费更多的时间。
7.4 函数的所有权机制
fn main() {
let s = String::from("hello");
// s 被声明有效
takes_ownership(s);
// s 的值被当作参数传入函数
// 所以可以当作 s 已经被移动,从这里开始已经无效
let x = 5;
// x 被声明有效
makes_copy(x);
// x 的值被当作参数传入函数
// 但 x 是基本类型,依然有效
// 在这里依然可以使用 x 却不能使用 s
} // 函数结束, x 无效, 然后是 s. 但 s 已被移动, 所以不用被释放
fn takes_ownership(some_string: String) {
// 一个 String 参数 some_string 传入,有效
println!("{}", some_string);
} // 函数结束, 参数 some_string 在这里释放
fn makes_copy(some_integer: i32) {
// 一个 i32 参数 some_integer 传入,有效
println!("{}", some_integer);
} // 函数结束, 参数 some_integer 是基本类型, 无需释放
基本数据类型被当做参数传递给其他方法之后依然有效,非基本数据类型传递给其他方法后则无效。
7.5 引用
fn main() {
let s1 = "Hello,world";
let s2 = &s1;
println!("s1 is {} , s2 is {}", s1, s2);
}
运行结果:
s1 is Hello,world , s2 is Hello,world
s1指向堆中的内容,s2指向s1。
fn main() {
let s1 = "Hello,world";
let s2 = &s1;
println!("s1 is {} , s2 is {}", s1, s2);
let s1 = s1.len();
println!("s1 is {} , s2 is {}", s1, s2);
}
运行结果:
s1 is Hello,world , s2 is Hello,world
s1 is 11 , s2 is Hello,world
此时s1变为常量,s2发现s1更改则本身直接指向堆中的内容
7.6 租赁
fn main() {
let s1 = String::from("hello");
let mut s2 = &s1;
let s3 = s1; //s1已经被移动给s3
print!("{}", s2);
}
运行结果:
error[E0505]: cannot move out of `s1` because it is borrowed
--> src\main.rs:4:14
|
3 | let mut s2 = &s1;
| --- borrow of `s1` occurs here
4 | let s3 = s1;
| ^^ move out of `s1` occurs here
5 | print!("{}", s2);
| -- borrow later used here
For more information about this error, try `rustc --explain E0505`.
正确写法:
fn main() {
let s1 = String::from("hello");
let mut s2 = &s1;//此处会有warn,因为s2未被使用就被重写
let s3 = s1;
s2 = &s3;
print!("{}", s2);
}
租赁者无法修改原来的值
fn main() {
let s1 = String::from("hello");
let s2 = &s1;
s2.push_str("world");
}
运行结果:
error[E0596]: cannot borrow `*s2` as mutable, as it is behind a `&` reference
--> src\main.rs:4:5
|
3 | let s2 = &s1;
| --- help: consider changing this to be a mutable reference: `&mut s1`
4 | s2.push_str("world");
| ^^^^^^^^^^^^^^^^^^^^ `s2` is a `&` reference, so the data it refers to cannot be borrowed as mutable
可以用&mut修饰可变的引用变量
fn main() {
let mut s1 = String::from("hello");
println!("{}",s1);
let s2 = &mut s1;//s2是可变的引用
s2.push_str(",world");
println!("{}",s1);
}
可变引用不可以多重赋值,防止并发下的碰撞
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;//错误,不可以多重赋值
println!("{}, {}", r1, r2);
8.切片类型
8.1 字符串切片
fn main() {
let s1 = String::from("helloworld");
let part1 = &s1[0..5];
let part2 = &s1[5..10];
println!("{} = {} + {}", s1, part1, part2);
}
…的含义
…y 等价于 0…y x… 等价于位置 x 到数据结束 … 等价于位置 0 到结束
被部分引用的变量不可以更改
fn main() {
let mut s1 = String::from("helloworld");
let s2 = &s1[..3];
s1.push_str("111");//报错
println!("s1 = {} ", s1);
println!("s2 = {} ", s2);
}
快速将String转化为&str的方法
let s1 = String::from("hello");
let s2 = &s1[..];
8.2 非字符串切片
fn main() {
let s1 = [1, 2, 3, 4, 5];
let s2 = &s1[..3];
for i in s2.iter() {
println!("{}", i);
}
}
9.结构体
9.1 定义结构体
struct student {
name: String,
age: i32,
sex: char
}
9.2 实例化结构体
fn main() {
let djy = student {
name: String::from("段金宇"),
age: 23,
sex: '男'
};
let syc = student {
name : String::from("abc"),
..djy
};
println!("{}",syc.name);
println!("age = {}",syc.age);
println!("sex = {}",syc.sex)
}
运行结果:
name = abc
age = 23
sex = 男
**“…djy”**是将其余未设置的值与“djy”的一致,但不允许所有值全部一致。
9.3 元组结构体
fn main() {
struct Color(u8, u8, u8);
let black = Color(0, 0, 0);
println!("black({},{},{})", black.0, black.1, black.2)
}
适用于经常使用的简单结构体,访问使用下标
9.4 输出整个结构体
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!("rect1 is {:?}", rect1);
}
{:?}:将整个结构体在同一行输出{:#?}:将整个结构体换行输出
9.5 结构体方法
struct Rectangle {
width: u32,
height: u32
}
impl Rectangle {
fn area(&self) -> u32 {
self.height * self.width
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!("area is {}", rect1.area())
}
运行结果:
area is 1500
多个参数的时候传入参数的时候不需要输入self
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.height * self.width
}
fn wider(&self, rect: &Rectangle) -> bool {
self.width > rect.width
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
let rect2 = Rectangle {
width: 40,
height: 50,
};
println!("{}", rect1.wider(&rect2));
}
输出结果:false
9.6 结构体函数
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn create(width: u32, height: u32) -> Rectangle {
Rectangle { width, height }
}
}
fn main() {
let rect1 = Rectangle::create(30, 50);
println!("{:#?}", rect1);
}
9.7 注意
所有权转移可能会破坏结构体的完整性
struct Dog {
name: String,
age: i8
}
fn main() {
let mydog = Dog {
name:String::from("wangcai"),
age:3,
};
let str = mydog.name;
println!("str={}", str);
println!("mydog: name={},age={}", mydog.name, mydog.age);//编译时出错
}
基本类型赋值时相当于把值复制,引用类型赋值时是将所有权转移,因此引用类型若要赋值时使用clone
let str = mydog.name.clone();
10.枚举类
10.1 枚举类定义
#[derive(Debug)]
enum Book {
Papery,
Electronic,
}
fn main() {
let book = Book::Papery;
println!("{:?}", book)
}
运行结果:
Papery
可以为枚举类成员添加元组属性描述:
#[derive(Debug)]
enum Book {
Papery(u32),
Electronic(String),
}
fn main() {
let book = Book::Papery(1001);
println!("{:?}", book)
}
输出结果:
Papery(1001)
如果想为属性命名,可以使用结构体语法
#[derive(Debug)]
enum Book {
Papery { index: u32 },
Electronic { URL: String },
}
fn main() {
let book = Book::Papery{index:1001};
println!("{:?}", book)
}
运行结果:
Papery { index: 1001 }
10.2 match
rust通过match语句来实现分支结构
#[derive(Debug)]
enum Book {
Papery { index: u32 },
Electronic { URL: String },
}
fn main() {
let book = Book::Papery { index: 1001 };
let book2 = Book::Electronic {
URL: String::from("http://www.123.com"),
};
match book2 {
Book::Papery { index } => {
println!("index is {}", index);
}
Book::Electronic { URL } => {
println!("URL is {}", URL);
}
}
}
例外情况用下划线表示:
fn main() {
let a = "abc";
match a {
"abc" => println!("{}", a),
_ => {}
}
}
10.3 Option枚举类
enum Option
some(T),
none,
}
fn main() {
let a = Option::some("hello");
let b: Option<&str> = Option::none;//初始为空的值,编译器不知道是什么类型,所以必须手动设置类型
match b {
Option::some(sth) => println!("{}", sth),
Option::none => println!("this is nothing!"),
}
}
运行结果:
this is nothing!
Option是rust默认引入的,可以省略Option::
fn main() {
let a = Some("hello");
let b: Option<&str> = None;
match b {
Some(sth) => println!("{}", sth),
None => println!("this is nothing!"),
}
}
10.4 if let
match方法的语法糖,比较枚举类的时候只能这样使用
#[derive(Debug)]
enum Book {
Papper { index: i32 },
Eletronic(String),
}
fn main() {
let book = Book::Papper { index: 1001 };
let ebook = Book::Eletronic(String::from("abc"));
if let Book::Papper { index } = ebook {
print!("this book is {:#?}", index)
} else if let Book::Eletronic(str) = ebook {
print!("this is ebook {}",str)
} else {
print!("this is ebook")
}
}
11.组织管理
模块的写法
mod nation {
pub mod gov {
pub fn gov(name: String) {
println!("gov is {}", name);
}
}
}
对模块的引用
crate::nation::gov::gov(String::from("china"));//绝对路径
nation::gov::gov(String::from("china"));//相对路径
默认是私有的,要公开要添加关键字:pub
模块中的结构体中的字段默认也是私有,但模块中的枚举类中的字段是公开的
12.错误处理
可恢复错误:Result
不可恢复错误:panic!
12.1 不可恢复错误
fn main() {
panic!("error occured");
println!("hello world")
}
运行结果:
//错误信息以及错误位置
thread 'main' panicked at 'error occured', src\main.rs:2:5
//通过 `RUST_BACKTRACE=1` 环境变量运行以显示回溯
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
12.2 可恢复错误
类似于java中的try…catch
在 Rust 中通过 Result
enum Result
Ok(T),
Err(E),
}
在 Rust 标准库中可能产生异常的函数的返回值都是 Result 类型的
use std::fs::File;
fn main() {
let f = File::open("hello.txt");
match f {
Ok(file) => println!("File succ!"),
Err(err) => println!("err")
}
}
输出:err
使用语法糖
use std::{f32::consts::E, fs::File};
fn main() {
let f = File::open("hello.txt");
if let Ok(file) = f {
print!("ok")
} else {
print!("err")
}
}
如果想使一个可恢复错误按不可恢复错误处理,Result类提供了两个办法:unwarp()和expect(message: &str)
use std::fs::File;
fn main() {
let f1 = File::open("hello.txt").unwrap();
let f2 = File::open("hello.txt").expect("Failed to open.");
}
12.3 可恢复的错误的传递
fn f(i: i32) -> Result
if i >= 0 { Ok(i) }
else { Err(false) }
}
fn main() {
let r = f(10000);
if let Ok(v) = r {
println!("Ok: f(-1) = {}", v);
} else {
println!("Err");
}
}
运行结果:
Ok: f(-1) = 10000
?操作符
fn f(i: i32) -> Result
if i >= 0 {
Ok(i)
} else {
Err(false)
}
}
fn g(i: i32) -> Result
let t = f(i)?;//将Result类非异常值取出,如果有异常就将Result返回出去
Ok(t)
}
fn main() {
let r = g(-10000);
if let Ok(v) = r {
println!("g(10000) = {}", v);
} else {
println!("Err")
}
}
? 符的实际作用是将 Result 类非异常的值直接取出,如果有异常就将异常 Result 返回出去。所以,? 符仅用于返回值类型为 Result
12.4 kind方法
类似于java中的try方法,将异常传递出去解决
use std::fs::File;
use std::io;
use std::io::Read;
fn read_text_from_file(path: &str) -> Result
let mut f = File::open(path)?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
fn main() {
let str_file = read_text_from_file("hello.txt");
match str_file {
Ok(s) => println!("{}", s),
Err(e) => match e.kind() {
io::ErrorKind::NotFound => {
println!("No such file");
}
_ => {
println!("Cannot read the file");
}
},
}
}
运行结果:
No such file
13.泛型与特性
13.1 声明格式
fn max
let mut max_index = 0;
let mut i = 1;
while i < array.len() {
if array[i] > array[max_index] {
max_index = i;
}
i += 1;
}
array[max_index]
}
13.2 结构体与枚举类中的泛型
x与y的类型相同
struct Point
x: T,
y: T,
}
fn main() {
let point = Point { x: 1, y: 2 };
let point1 = Point { x: 1.0, y: 2.0 };
}
当x与y的类型可能不同时
struct Point
x: T1,
y: T2,
}
fn main() {
let point = Point { x: 1, y: 2.0 };
}
枚举类中使用泛型
enum Option
Some(T),
None
}
enum Result
Ok(T),
Err(E)
}
13.3 特性(trait)
特性(trait)概念接近于Java 中的接口(Interface),都是一种行为规范
trait Descriptive {
fn describe(&self);
}
impl Descriptive for Persion {
fn describe(&self) {
print!("{}", self.name)
}
}
impl Descriptive for Dog {
fn describe(&self) {
print!("the host of {} is {}", self.name,self.host.name)
}
}
fn main() {
let people = Persion {
name: String::from("djy"),
age: 30,
};
let dog = Dog {
host: people,
age: 3,
name: String::from("wang"),
};
dog.describe();
}
13.4 特性作为参数
fn output(object:impl Descriptive) {
object.describe()
}
等效写法
fn output
object.describe()
}
在main中调用
output(dog);
输出结果:
the host of wang is djy
任何实现了 Descriptive 特性的对象都可以作为这个函数的参数,这个函数没必要了解传入对象有没有其他属性或方法,只需要了解它一定有 Descriptive 特性规范的方法就可以了。当然,此函数内也无法使用其他的属性与方法。
特性作类型表示时如果涉及多个特性,可以用 + 符号表示
fn notify(item: impl Summary + Display)
fn notify
复杂的实现关系可以使用where关键词简化
fn some_function
//可以简化为
fn some_function
where T: Display + Clone,
U: Clone + Debug
泛型取最大值案例
trait Comparable {
fn compare(&self, object: &Self) -> i8;
}
impl Comparable for f64 {
fn compare(&self, object: &f64) -> i8 {
if &self > &object {
return 1;
} else if &self < &object {
return -1;
} else {
return 0;
}
}
}
fn max
let mut max_index = 0;
let mut i = 0;
while i < array.len() {
if array[i].compare(&array[max_index]) > 0 {
max_index = i;
}
i = i + 1;
}
&array[max_index]
}
fn main() {
let a = [1.0,6.0,1.0,5.0,2.0];
println!("the max value is {}",max(&a))
}
输出结果:
the max value is 6
13.5 特性作为返回值
当特性作为返回值得时候所有可能返回的类型必须只有一个(以下案例错误)
struct A {}
struct B {}
trait Des {}
impl Des for B {}
impl Des for A {}
fn f(s: bool) -> impl Des {
if s {
return A {};
} else {
return B {};
}
}
fn main() {}
运行结果:
error[E0308]: mismatched types
--> src\main.rs:10:16
|
6 | fn f(s: bool) -> impl Des {
| -------- expected `_` because of return type
...
10 | return B {};
| ^^^^ expected struct `A`, found struct `B`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `greeting` due to previous error
13.6 有条件的实现方法
对于泛型类来说,有时我们需要区分一下它所属的泛型已经实现的方法来决定它接下来该实现的方法,这段代码声明了A类型必须在T已经实现了B和C的前提下才能有效的实现此impl块
struct D {}
trait Imb {
fn b(&self);
}
trait Imc {
fn c(&self);
}
impl Imb for D {
fn b(&self) {
print!("实现了b");
}
}
impl Imc for D {
fn c(&self) {
print!("实现了c");
}
}
struct A
name: T,
}
impl
fn d(&self) {
print!("ok");
}
}
fn main() {
let s = D {};
let a = A { name: s };
a.d();
}
14.rust生命周期
&str与String
比较字符串长度
fn longer(s1: String, s2: String) -> String {
if s1.len() > s2.len() {
s1
} else { s2 }
}
fn main() {
let s1 = String::from("aaa");
let s2 = String::from("aa");
println!("{}", longer(s1, s2));
}
错误写法:
fn longer(s1: &str, s2: &str) -> &str {
if s1.len() > s2.len() {
s1
} else { s2 }
}
fn main() {
let s1 = "aaa";
let s2 = "aa";
println!("{}", longer(&s1, &s2));
}
报错信息:
|
1 | fn longer(s1: &str, s2: &str) -> &str {
| ---- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `s1` or `s2`
help: consider introducing a named lifetime parameter
|
1 | fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str {
| ++++ ++ ++ ++
14.1 String 和 &str的区别(未完全懂)
使用&str传参
fn f(name: &str) {
println!("{}", name)
}
fn main() {
let s1 = "aaa";
f(s1);
println!("{}", s1)
}
使用String传参
fn f(name: String) {
println!("{}", name)
}
fn main() {
let s1 = "aaa".to_string();
f(s1);//编译不通过,可以使用&s1
println!("{}", s1)
}
在rust中,str是动态长度的UTF-8的不可变序列,在内存中的某个位置,因此只有指针和长度,一般多使用&str对字符串切片进行引用。&str可以理解为字符串视图
str只是一行u8字节,并保证它形成有效的UTF-8。行有多大?在运行时之前没人知道,因此无法将其存储在变量中。
let s1 = "aaa".to_string();
let s2 = s1[..];
用该方法可以将String转换为str但实际上并不能通过编译,因为通过该方法得到的str在编译时并不知道具体大小。
而String是一个可增长的字节数组,拥有字符串的所有内容,如长度,容量,指针等,可进行追加等操作;
字符串长度比较
fn f(s1: &str, s2: &str) -> &str {
if s1.len() > s2.len() { s1 } else { s2 }
}//编译无法通过,原因是返回值引用可能会返回过期的引用
fn f1(s1: String, s2: String) -> String {
if s1.len() > s2.len() { s1 } else { s2 }
}
第一段程序中虽然经过了比较,但 r 被使用的时候源值 s1 和 s2 都已经失效了。当然我们可以把 r 的使用移到 s1 和 s2 的生命周期范围以内防止这种错误的发生,但对于函数来说,它并不能知道自己以外的地方是什么情况,它为了保障自己传递出去的值是正常的,必选所有权原则消除一切危险,所以 longer 函数并不能通过编译。
14.2 声明周期注释
生命周期注释是描述引用生命周期的办法。
虽然这样并不能够改变引用的生命周期,但可以在合适的地方声明两个引用的生命周期一致。
生命周期注释用单引号开头,跟着一个小写字母单词:
&i32 // 常规引用
&'a i32 // 含有生命周期注释的引用
&'a mut i32 // 可变型含有生命周期注释的引用
用声明周期注释改造longer函数
fn f<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() > s2.len() { s1 } else { s2 }
}
14.3 结构体中使用字符串切片引用
fn main() {
struct Str<'a> {
content: &'a str,
}
let c = Str { content: "aaa" };
println!("{}", c.content)
}
运行结果:
aaa
如果对结构体Str有方法定义
impl<'a> Str<'a> {
fn get_content(&self) -> &str {
self.content
}
}
返回值可加生命周期注解也可不加,因为早期Rust不支持声明周期自动判断
14.4 静态生命周期
生命周期注释有一个特别的:'static 。所有用双引号包括的字符串常量所代表的精确数据类型都是 &'static str ,'static 所表示的生命周期从程序运行开始到程序运行结束。
15.文件与IO
15.1 命令行输入
use std::io::stdin;
fn main() {
let mut str_buf = String::new();
stdin().read_line(&mut str_buf).expect("Fild to read line.");
println!("{}", str_buf);
}
目前 Rust 标准库还没有提供直接从命令行读取数字或格式化数据的方法,我们可以读取一行字符串并使用字符串识别函数处理数据。
15.2 文件读取
读取文本中的字符串
use std::fs;
fn main() {
let file = fs::read_to_string("D:\\a.txt").unwrap();
println!("{:?}", file)
}
但如果要读取的文件是二进制文件,我们可以用 std::fs::read 函数读取 u8 类型集合:
use std::fs;
fn main() {
let content = fs::read("D:\\text.txt").unwrap();
println!("{:?}", content);
}
文件流读取
use std::fs;
use std::io::Read;
fn main() {
let mut buffer = [0u8; 100];
let mut file = fs::File::open("D:\\a.txt").unwrap();
file.read(&mut buffer).unwrap();
println!("{:?}", buffer);
file.read(&mut buffer).unwrap();
println!("{:?}", buffer);
}
15.3 文件写入
一次性写入
use std::fs;
fn main() {
fs::write("D:\\b.txt", "睡大觉").unwrap();
}
运行后文件会被重写为”睡大觉“,因此一次性写入要谨慎使用。如果文件不存在则会创建文件。
流方式写入
use std::fs::File;
use std::io::Write;
fn main() {
let mut file = File::create("D:\\c.txt").unwrap();
file.write(b"sadad").unwrap();//b是撒子东西?
}
16.集合与字符串
16.1 向量
向量是线性表,类似于List
向量的创建
let mut vector = Vec::new();
vector = vec![1, 2, 3, 4];
追加(push):
fn main() {
let mut vector = vec![1, 2, 3, 4];
vector.push(5);
vector.push(6);
vector.push(7);
println!("{:?}", vector)
}
结果:
[1, 2, 3, 4, 5, 6, 7]
取值(get):
fn main() {
let mut vector = vec![1, 2, 3, 4];
match vector.get(10) {
Some(value) => println!("{}", value),
None => println!("None")
}
}
因为向量的长度无法从逻辑上推断,get 方法无法保证一定取到值,所以 get 方法的返回值是 Option 枚举类,有可能为空。
这是一种安全的取值方法,但是书写起来有些麻烦。如果你能够保证取值的下标不会超出向量下标取值范围,你也可以使用数组取值语法:
fn main() {
let mut vector = vec![1, 2, 3, 4];
println!("{}", vector[1])
}
遍历向量:
fn main() {
let mut vector = vec![1, 2, 3, 4];
for i in vector {
println!("{}", i)
}
}
遍历向量中需要改变原来的值
fn main() {
let mut vector = vec![1, 2, 3, 4];
for i in &mut vector {
*i += 3
}
for i in vector {
println!("{}", i)
}
}
16.2 字符串
新建:new()基础类转换成字符串:to_string()包含UTF-8的字符串:from()追加字符串:push_str()追加字符:push()+号拼接字符串(也可用于字符串切片)format!宏
use std::fmt::format;
fn main() {
let mut string1 = String::from("aaa");
let mut string2 = String::from("bbb");
let s = format!("{}-{}", string1, string2);
println!("{}", s)
}
字符串长度:len()转为字符数组:chars()取单个字符:chars().nth()
16.3 映射表
新建:HashMap::new() 插入:insert() 安全插入:entry(“a”).or_insert(“b”) 如果没有a键,则添加a键并设置值为b,如果已有a键则跳过 迭代器:iter() 根据key获取value: 如果确定有key值则可以:
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert("s", "b");
if let Some(x) = map.get_mut(&"s") {
*x = "v"
}
println!("{:?}", map.get("s"))
}
16.面向对象
16.1 封装
main.rs
mod second;
use second::Second;
fn main() {
let sec = Second::new(String::from("段金宇"),13);
sec.public_method();
}
second.rs
pub struct Second {
name: String,
age: i32,
}
impl Second {
pub fn new(my_name: String, my_age: i32) -> Second {
Second { name: my_name, age: my_age }
}
pub fn public_method(&self) {
println!("共有方法并且name = {}", &(self.name));
self.private()
}
fn private(&self) {
println!("私有方法age = {}", &(self.age))
}
}
输出:
共有方法并且name = 段金宇 私有方法age = 13
注意:
Second结构体虽然是pub但只能被引用,依然不能被直接声明如果想要可以直接声明则需要将结构体中的每个属性前都声明pub
17.并发编程
17.1 线程
use std::thread;
use std::time::Duration;
fn spawn_function() {
for i in 0..5 {
println!("this is a son thread {}", i);
thread::sleep(Duration::from_millis(1))
}
}
fn main() {
thread::spawn(spawn_function);
for i in 0..5 {
println!("this is a main thread {}", i);
thread::sleep(Duration::from_millis(1))
}
}
17.2 闭包写法
|参数1, 参数2, ...| -> 返回值类型 {
// 函数体
}
上面的多线程改写:
use std::thread;
use std::time::Duration;
fn main() {
thread::spawn(|| {
for i in 0..5 {
println!("this is a son thread {}", i);
thread::sleep(Duration::from_millis(1))
}
});
for i in 0..5 {
println!("this is a main thread {}", i);
thread::sleep(Duration::from_millis(1))
}
}
闭包可以自动判断类型,以下两种写法均可
fn main() {
let inc = |num:i32| -> i32 {
num + 1
};
println!("{:?}", inc(1));
}
fn main() {
let inc = |num| {
num + 1
};
println!("{:?}", inc(1));
}
17.2 join方法
join方法可以强制使子线程结束后再停止运行程序
use std::thread;
use std::time::Duration;
fn main() {
let td = thread::spawn(|| {
for i in 0..5 {
println!("this is a son thread {}", i);
thread::sleep(Duration::from_millis(1))
}
});
for i in 0..5 {
println!("this is a main thread {}", i);
thread::sleep(Duration::from_millis(1))
}
td.join().unwrap()
}
运行结果:
this is a main thread 0
this is a son thread 0
this is a main thread 1
this is a son thread 1
this is a son thread 2
this is a main thread 2
this is a son thread 3
this is a main thread 3
this is a son thread 4
this is a main thread 4
如果在声明线程后直接使用join方法则会使子线程运行结束后再运行main
use std::thread;
use std::time::Duration;
fn main() {
thread::spawn(|| {
for i in 0..5 {
println!("this is a son thread {}", i);
thread::sleep(Duration::from_millis(1))
}
}).join().unwrap();
for i in 0..5 {
println!("this is a main thread {}", i);
thread::sleep(Duration::from_millis(1))
}
}
运行结果:
this is a son thread 0
this is a son thread 1
this is a son thread 2
this is a son thread 3
this is a son thread 4
this is a main thread 0
this is a main thread 1
this is a main thread 2
this is a main thread 3
this is a main thread 4
17.3 move强制转移所有权
在子线程中尝试使用当前函数的资源,这一定是错误的!因为所有权机制禁止这种危险情况的产生,它将破坏所有权机制销毁资源的一定性。我们可以使用闭包的 move 关键字来处理:
use std::thread;
use std::time::Duration;
fn main() {
let s = 5;
thread::spawn(move || {
for i in 0..5 {
println!("this is a son thread {} + {}", i, s);
thread::sleep(Duration::from_millis(1))
}
}).join().unwrap();
for i in 0..5 {
println!("this is a main thread {}", i);
thread::sleep(Duration::from_millis(1))
}
}
17.4 消息传递
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let val = String::from("hello");
tx.send(val).unwrap()
});
println!("{}", rx.recv().unwrap())
}
注意要用move,tx和rx均在main中创建
柚子快报邀请码778899分享:开发语言 rust基础学习
发表评论