rust 语言入门
1.数据类型
struct Point {
x : i32,
y : u32,
msg: String,
}
fn simple_func(){
let p = Point{x:25, y: 25, msg: String::from("point") };
let p1 = p;
println!("{} {} {}", p1.x, p1.y, p1.msg);
}
fn main() {
simple_func();
}
1.1 整数型
- 整数型有 isize 与 usize 两种类型,用来衡量数据大小。如上图
i32
为 32位的isize
类型。 - 整数型定义
位长 | isize | usize |
---|---|---|
8bit | i8 | u8 |
16bit | i16 | u16 |
32bit | i32 | u32 |
64bit | i64 | u64 |
128bit | i128 | u128 |
1.2 浮点类型
- 浮点类型有 32精度 和 64精度。定义分别为
x:f64, y: f32
。 - 默认是64精度位
let x = 100.00; // 64精度位
1.3 字符型
- 字符型关键字用
char
标识
不讲太多,不好吸收(还有布尔型 与 复合类型)
2 所有权
2.1 所有权转移
fn simple_func(){
let p = Point{x:25, y: 25, msg: String::from("point") };
let p1 = p;
/**
value borrowed here after move
p 所有权被移除
*/
// println!("{} {} {}", p.x, p.y, p.msg);
/**
所有权属于 p1
*/
println!("{} {} {}", p1.x, p1.y, p1.msg);
}
Point
这数据所有权属于p
对象p1 = p
之后,Point
的所有权转移到 p1Point
这个数据的所有权只能属于一个变量- 一个数据(java 所说的对象)所有权只属于一个变量(指针)
- 由于所有权的存在:一块内存只有一个所有权,当所有权变量(
p
,p1
)不存在了,Pont
的内存就会被编译器回收。
2.2 引用
2.2.1 只读引用
- 只读不能修改,避免多线程对于单个对象修改造成的线程不安全问题
let p = Point{x:25, y: 25, msg: String::from("point") }; // read-only
p.x = 35; // 编译不通过
println!("{} {} {}", p.x, p.y, p.msg);
mut
关键字声明可变
let mut p = Point{x:25, y: 25, msg: String::from("point") };
p.x = 35;
println!("{} {} {}", p.x, p.y, p.msg);
```#### 2.3 借用 borrowing
##### 2.3.1 所有权回收
```go
fn simple_borrow(){
let p1 = Point{x : 25, y : 25};
fn1(p1);// 所有权转移 p 转移-> 形参p
print!("{} {}",p1.x, p1.y)
}
fn fn1(p: Point){} // p 所有权转移,函数声明周期结束了,p 指向的数据(内存)被回收
p1
对象会在函数结束时候被回收。编译不通过:move occurs becausep1
has typePoint
, which does not implement theCopy
trait- 由此出现
borrowing
的概念。
2.3.2 所有权借用
- 函数
形参
借用(borrowing)传参
指向对象的所有权
- 使用
&
借用类型 不要与 go 语言与 c++ 的关键字混起来记
fn simple_borrow(){
let p1 = Point{x : 25, y : 25};
fn1(&p1); //& 对象(内存或数据)所有权暂时借用给函数内的的形式参数,不要跟go或者c的混着记
print!("{} {}",p1.x, p1.y)
}
fn fn1(_p: &Point){}// 函数结束后,_p 指向数据的所有权还给 p1
2.3.3 可变借用 borrowing mutable
- 定义的对象是可变的对象
mut
与 可变的借用对象&mut
fn simple_borrow(){
let mut p1 = Point{x : 25, y : 25};
fn1(&mut p1); //& 对象(内存或数据)所有权暂时借用给函数内的的形式参数,不要跟go或者c的混着记
print!("{} {}",p1.x, p1.y)
}
fn fn1(_p: &mut Point){
_p.x = 35
}// 函数结束后,_p 指向数据的所有权还给 p1
数据竞争
的产生条件
- 两个或更多指针同时访问同一数据。
- 至少有一个指针被用来写入数据。
- 没有同步数据访问的机制。
为了避免 数据竞争
,mut
可变引用使用有如下编译规则
- 对一个数据的引用的
作用域
声明,在同一个作用域内,不能同时
拥有两个或以上可变借用(不能同时有两个mut
引用修改数据导致数据不一致) - 同一作用域内,不能
同时
存在 不可变引用借用 与 可变引用借用。
错误
fn main() {
let mut s = String::from("hello");
let r1 = &s; // 没问题
let r2 = &s; // 没问题
let r4 = &mut s; // 有问题
println!("{} and {} and {}", r1, r2, r3);// 问题出现在这个调用,即使用了 s 的不可变借用 与 可变借用(r1,r2,)
}
正确
fn main() {
let mut s = String::from("hello");
let r1 = &s; // 没问题
let r2 = &s; // 没问题
println!("{} and {}", r1, r2);
// 此位置之后 r1 和 r2 不再使用
let r3 = &s; // 没问题
println!("{}", r3);
}
2.3.4 悬垂指针
- 在具有指针的语言中,当指针指向的内存被释放以后而错误产生一个悬垂指针。(可以理解为
java
内的空指针) - 在 Rust 中编译器确保引用永远也不会变成悬垂状态:当你拥有一些数据的引用,编译器确保数据不会在其引用之前离开作用域。(内存会迟于引用被释放)
3 智能指针
3.1 Box指针 保证分配在堆上
- 数据所有权一般在函数的生命周期内,数据一般分配在方法栈内
- 数据写在堆内存上
- 引用独占所有权,该数据只能有一个应用指向
3.2 Rc 引用计数,单线程,只读
- 引用计数指针,该指针保存着这份内存数据的引用计数
- 一份数据所有权可以给多个引用指向,通过克隆的形式。
该形式与java的克隆概念不同,rust的克隆是多个引用指向同一个数据
- 当所有应用不再指向这份数据的时候,这份数据被回收
fn rc_new_thread(){
let p = Rc::new(Point{x:25, y:25});
let p1 = Rc::clone(&p); // 克隆一份
let p2 = Rc::clone(&p); // 克隆一份
println!("p: {} {}", (*p).x , (*p).y);
println!("p1:{} {}", (*p1).x , (*p1).y);
println!("p2:{} {}", (*p2).x , (*p2).y);
}
3.3 Arc 多线程引用计数,只读
- 原子性引用计数,该数据可以通过
move
转移所有权到多个线程中。
fn thread_Arc_two() {
let p = Arc::new(Point{x:30, y:30});
let p1 = Arc::clone(&p);
let p2 = Arc::clone(&p);
let h1 = thread::spawn(move ||{ // move 对象所有权转移
println!("{}", p1.x);
thread::sleep(Duration::from_millis(100));
});
let h2 = thread::spawn(move ||{ // move 对象所有权转移
println!("{}", p2.x);
thread::sleep(Duration::from_millis(100));
});
h1.join();
h2.join();
}
3.4 Mutex 互斥指针
- 智能指针指向特定的数据,该指针内有一把锁,只允许一个引用修改数据
- 将该数据传递到多线程去修改,需要通过
Arc
指针封装,可以使多个引用指向该Mutex
指针。这样多线程也可以获取到Mutex
指针去修改这份数据
fn thread_two_write(){
let mut p_mutex = Arc::new(Mutex::new(Point{x:0, y:0}));
let p1 = Arc::clone(&p_mutex);
let p2 = Arc::clone(&p_mutex);
thread::spawn(move ||{
let mut p = p1.lock().unwrap();
p.x += 1;
println!("{} {}", p.x, p.y)
}).join();
thread::spawn(move ||{
let mut p = p2.lock().unwrap();
p.x += 1;
println!("{} {}", p.x, p.y)
}).join();
}