我的总结:如果是一些基本类型能够自动copy的,那么这种数据作为函数参数的时候,可以不用考虑&,并且在函数执行完后,仍然能够正常使用,如果参数不是copy类型,那么在函数调用完后想继续使用,或者copy特别耗性能,就使用&。
在 Rust 中,对于一些实现了 Copy trait 的基本类型,可以直接传递值而不需要担心所有权转移的问题。这些类型在传递给函数时会自动复制,函数内部和外部都可以独立地使用这些值。
Rust 中的基本类型(如整数、浮点数、布尔值、字符等)和一些简单的复合类型(如固定大小的数组和元组)默认实现了 Copy trait。这意味着当它们被传递给函数时,会进行按位复制(bitwise copy),而不会转移所有权。
对于非 Copy 类型(如 String、Vec 等),传递值会转移所有权,函数调用后原来的变量将不可用。如果你需要在函数调用后继续使用原来的数据,可以使用引用。
大数据结构,即使它们实现了 Copy trait,复制的开销也可能很大。在这种情况下,使用引用可以避免不必要的复制,提高性能。
Rust 中的 ? 操作符是用于简化错误处理的,它可以在函数返回 Result 或 Option 类型时,将错误自动传播给调用者。当遇到错误时,? 操作符会提前返回错误,而不会继续执行后续代码。
use std::fs::File;
use std::io::{self, Read};
use std::num::ParseIntError;
// 读取文件内容
fn read_file_content(file_path: &str) -> Result<String, io::Error> {
let mut file = File::open(file_path)?;
let mut content = String::new();
file.read_to_string(&mut content)?;
Ok(content)
}
// 解析字符串中的数字
fn parse_number(content: &str) -> Result<i32, ParseIntError> {
let number: i32 = content.trim().parse()?;
Ok(number)
}
// 读取文件并解析数字
fn read_and_parse(file_path: &str) -> Result<i32, Box<dyn std::error::Error>> {
let content = read_file_content(file_path)?;
let number = parse_number(&content)?;
Ok(number)
}
fn main() {
match read_and_parse("example.txt") {
Ok(number) => println!("The number is: {}", number),
Err(e) => eprintln!("Error: {}", e),
}
}
如果想要捕获的话,就去掉?通过match来匹配是Ok还是Err。
fn print(n: &[f64]) {
for elt in n {
println!("{}", elt);
}
}
#[test]
pub fn print_test(){
let v: Vec<f64> = vec![0.0, 0.707, 1.0, 0.707];
let a: [f64; 4] = [0.0, -0.707, -1.0, -0.707];
print(&v[0..2]);
print(&a[2..]);
}
let mut s = "hello";
s[0] = 'c';
vec在堆中访问,string也在堆中,但是数组在栈里。
let ab = "good";
let ad = &ab;
let ae = "good".to_string();
let af = &ae;
println!("{}", ab);
println!("{:p}", ad);
println!("{}", ae);
println!("{:p}", af);
在 Rust 中,字符串和数组有多种形式,每种形式在内存管理和使用方式上都有所不同。以下是对 `String`、数组 `[T]`、`Vec<T>` 以及引用类型 `&str`、`&[T]` 和 `&Vec<T>` 之间关系的详细解释:
- `String` 是一个可变的、拥有所有权的字符串类型,存储在堆上。
- 它是动态分配的,可以根据需要增长。
- 适用于需要修改字符串内容或动态生成字符串的场景。
- `&str` 是一个不可变的字符串切片,通常指向二进制数据中的一部分。
- 它可以指向字符串字面值(如 `"hello"`)或 `String` 的一部分。
- 适用于只读字符串数据,不需要修改内容的场景。
- 数组是固定大小的,存储在栈上。
- `[T; N]` 表示一个包含 `N` 个元素的数组,每个元素的类型为 `T`。
- 适用于已知大小的数据集合。
- `&[T]` 是一个不可变的数组切片,可以引用数组或向量的一部分。
- 切片不拥有数据,只是对数据的一个视图。
- 适用于需要只读访问数组或向量部分数据的场景。
- `Vec<T>` 是一个可变的、拥有所有权的动态数组类型,存储在堆上。
- 它可以根据需要增长或缩减。
- 适用于需要动态调整大小的数据集合。
- `&Vec<T>` 是一个对向量的不可变引用。
- 它不拥有数据,只是对向量的一个视图。
- 适用于需要只读访问整个向量的场景。
1. String和 &str
`String` 可以通过 `as_str()` 方法转换为 `&str`。
可以通过 `&` 操作符将 `String` 转换为 `&str`。
let s = String::from("hello");
let s_slice: &str = &s; // 转换为 &str
2. 数组 `[T; N]` 和切片 `&[T]`
数组可以通过 `&` 操作符转换为切片。
切片是对数组的一部分或全部的引用。
let arr = [1, 2, 3, 4];
let slice: &[i32] = &arr; // 转换为 &[i32]
3. `Vec<T>` 和切片 `&[T]`
Vec<T>` 可以通过 `as_slice()` 方法转换为切片。
可以通过 `&` 操作符将 `Vec<T>` 转换为切片。
let v = vec![1, 2, 3, 4];
let slice: &[i32] = &v; // 转换为 &[i32]
4. Vec<T> 和 &Vec<T>
Vec<T>可以通过 & 操作符转换为 &Vec<T>。
let v = vec![1, 2, 3, 4];
let v_ref: &Vec<i32> = &v; // 转换为 &Vec<i32>
示例代码
fn main() {
// String 和 &str
let s = String::from("hello");
let s_slice: &str = &s; // String 转换为 &str
// 数组和切片
let arr = [1, 2, 3, 4];
let arr_slice: &[i32] = &arr; // 数组转换为切片
// Vec 和切片
let v = vec![1, 2, 3, 4];
let v_slice: &[i32] = &v; // Vec 转换为切片
// Vec 和 &Vec
let v_ref: &Vec<i32> = &v; // Vec 转换为 &Vec
}
总结
以下是一些常用的方法,用于向 String 添加数据,从而触发动态增长:
let mut ah = String::from("good");
ah.push_str(" job");
let aj = " for me";
ah = ah + &aj;
print!("{}", ah);
Rust 的解决方案是为这些情况提供一些类似字符串的类型。
任何在丢弃值时需要做一些特殊操作的类型都不能是 Copy 类型:Vec 需要释放自身元素、File 需要关闭自身文件句 柄、MutexGuard 需要解锁自身互斥锁,等等。对这些类型进行逐位 复制会让我们无法弄清哪个值该对原始资源负责。
但是编译器不能自己判断是Copy类型,比如:
struct Person{
age: u32
}
虽然Person只有一个u32,但仍然不是Copy类型,需要手动声明:
#[derive(Copy)]
struct Person{
age: u32
}
但这样有问题,如果一个结构体字段不全是Copy类型,则无法这样做:
至于为什么符合条件的用户定义类型不能自动成为 Copy 类型呢?
这是因为类型是否为 Copy 对于在代码中使用它的方式有着重大影响:Copy 类型更灵活,因为赋值和相关操作不会把原始值变成未初始化状态。 但对类型的实现者而言,情况恰恰相反:Copy 类型可以包含的类型 非常有限,而非 Copy 类型可以在堆上分配内存并拥有其他种类的资 源。因此,创建一个 Copy 类型代表着实现者的郑重承诺:如果以后 确有必要将其改为非 Copy 类型,则使用它的大部分代码可能需要进 行调整。
vec的clone是深拷贝,如果vec内的item是引用,那么copy的也是引用,不会递归clone,而Rc引用直接就是copy的引用。
let s: Rc<String> = Rc::new("shirataki".to_string());
let t: Rc<String> = s.clone();
let u: Rc<String> = s.clone();
共享引用是 Copy 类型。可变引用不是 Copy 类型。只要存在对一个值的共享引 用,即使是它的拥有者也不能修改它,该值会被锁定。在 Rust 中 要使用 & 运算符和 * 运算符来创建引用(借用)和追踪引用(解引 用),不过 . 运算符不需要做这种转换,它会隐式借用和解引用。
判断引用是否相等
assert!(rx == ry);
assert!(!std::ptr::eq(rx, ry));
Rust 允许借用任意种类的表达式结果值的引用:
fn factorial(n: usize) -> usize {
(1..n+1).product()
}
let r = &factorial(6);
// 数学运算符可以“看穿”一层引用 assert_eq!(r + &1009, 1729);
pub fn test_lifetime <'static> (p: &'static i32){
}
这个其实没啥好说的,约束了p引用不能是指向'a的生命周期的引用,因为没有返回值,所以不用约束返回值的生命周期一致。假如代码:
let i = 3;
test_lifetime(&i);
这段代码会报错。
pub fn test_lifetime_response <'a> (p: &'a i32) -> &'a i32{
&3
}
这个时候不仅约束了p的生命周期,还有返回值的生命周期也得和p一致。假如代码:
let zz; {
let zw = 3;
zz = test_lifetime_response(&zw);
}
println!("{}", *zz);
这样的代码报错,因为入参数的p和返回值的生命周期不一致。
每当一个引用类型出现在另一个类型的定义中时,必须写出它的生命周期。
struct S<'a, 'b> {
r1: &'a i32,
r2: &'b i32,
}
fn create_s<'a, 'b>(x: &'a i32, y: &'b i32) -> S<'a, 'b> {
S { r1: x, r2: y }
}
let value1 = 10;
let result;
{
let value2 = 20;
let s = S{r1:&value1, r2:&value2};
result = s.r1;
}
println!("result {}", *result);
上面的代码能够通过运行,是因为入参数a、b的生命周期不同,如果再套一层也会报错,因为value1的生命周期已经结束:
let result;
{
let value1 = 10;
{
let value2 = 20;
let s = S{r1:&value1, r2:&value2};
result = s.r1;
}
}
println!("result {}", *result);
如果改成相同生命周期同样会报错,因为r2的生命周期和r1并不相同:
struct S<'a> {
r1: &'a i32,
r2: &'a i32,
}
fn create_s<'a, 'b>(x: &'a i32, y: &'a i32) -> S<'a> {
S { r1: x, r2: y }
}
Rust 中的每个类型都有生 命周期,包括 i32 和 String。它们大多数是 'static 的,这意 味着这些类型的值可以一直存续下去,例如,Vec<i32> 是自包含 的,在任何特定变量超出作用域之前都不需要丢弃它。但是像 Vec<&'a i32> 这样的类型,其生命周期就必须被 'a 涵盖,也就 是说必须在引用目标仍然存续的情况下丢弃它。
let mut x = 10;
let r1 = &x;
let r2 = &x; // 正确:允许多个共享借用
x += 10; // 错误:不能赋值给`x`,因为它已被借出
let m = &mut x;
let mut y = 20;
let m1 = &mut y;
let m2 = &mut y; // 错误:不能多次借入为可变引用
let z = y; // 错误:不能使用`y`,因为它涵盖在已借出的可变引用的生命周 期内
println!("{}, {}, {}", m1, m2, z); // 在这里使用这些引用
let mut w = (107, 109);
let r = &w;
let r0 = &r.0;// 正确:把共享引用重新借入为共享引用
//let m1 = &mut r.1;// 错误:不能把共享引用重新借入为可变
println!("{}", r0);
let mut v = (136, 139);
let m = &mut v;
let m0 = &mut m.0; // 正确: 从可变引用中借入可变引用
*m0 = 137;
let r1 = &m.1;// 正确: 从可变引用中借入共享引用,并且不能和m0重
v.1; // 错误:禁止通过其他路径访问
println!("{}", r1);// 可以在这里使用r1
结构体默认情况下是私有的,仅在声明它们 的模块及其子模块中可见。对于方法调用和字段访问,Rust 会自动从 Box、Rc、Arc 等指针类 型中借入引用,因此 &self 和 &mut self 几乎总是(偶尔也会用 一下 self)方法签名里的正确选择。
//TODO
一个生命周期的范型结构体
struct MyStruct<'a, T> {
value: &'a T,
}
带多个生命周期的范型结构体:
struct MultipleRefs<'a, 'b, T> {
first: &'a T,
second: &'b T,
}
impl<'a, 'b, T> MultipleRefs<'a, 'b, T> {
fn new(first: &'a T, second: &'b T) -> Self {
MultipleRefs { first, second }
}
fn get_first(&self) -> &'a T {
self.first
}
fn get_second(&self) -> &'b T {
self.second
}
}
fn main() {
let x = 10;
let y = 20;
let refs = MultipleRefs::new(&x, &y);
println!("First: {}", refs.get_first());
println!("Second: {}", refs.get_second());
}
与结构体一样,编译器能为你实现 == 运算符等特性,但你必须明确 提出要求
#[derive(Copy, Clone, Debug, PartialEq)] enum RoughTime {
InThePast(TimeUnit, u32),
JustNow,
InTheFuture(TimeUnit, u32),
}
match n、other、_。元组也可以、slice也可以,结构体也行:
match account {
Account { ref name, ref language, .. } => {
ui.greet(name, language);
ui.show_settings(&account); // 正确
}
}
可变引用也行:
match line_result {
Err(ref err) => log_error(err),
Ok(ref mut line) => {
trim_comments(line);
handle(line);
}
}
这特么也行
match sphere.center() {
&Point3d { x, y, z } => ...
}
在 Rust 中,`match` 表达式可以用于匹配引用类型。这在处理借用的数据时非常有用。下面是一些常见的场景和示例,展示如何在 `match` 表达式中匹配引用。
### 1. 匹配引用类型
假设我们有一个整数的引用,我们可以使用 `match` 来匹配它:
```rust
fn main() {
let x = 5;
let y = &x;
match y {
&val => println!("Got a value: {}", val),
}
}
```
在这个例子中,`y` 是一个对 `x` 的引用。我们在 `match` 表达式中使用 `&val` 模式来解引用 `y` 并将其值绑定到 `val`。
### 2. 使用 `ref` 关键字匹配引用
在某些情况下,你可能希望保留引用而不是解引用。这时可以使用 `ref` 关键字:
```rust
fn main() {
let x = 5;
let y = &x;
match y {
ref val => println!("Got a reference to value: {}", val),
}
}
```
在这个例子中,我们使用 `ref val` 模式来匹配引用并将其绑定到 `val`,这样我们就可以继续使用引用而不是解引用后的值。
### 3. 匹配可变引用
如果你有一个可变引用,可以使用 `ref mut` 关键字来匹配它:
```rust
fn main() {
let mut x = 5;
let y = &mut x;
match y {
ref mut val => {
**val += 1;
println!("Got a mutable reference to value: {}", val);
},
}
}
```
在这个例子中,我们使用 `ref mut val` 模式来匹配可变引用并将其绑定到 `val`。然后我们可以通过 `*val` 来修改引用指向的值。
### 4. 在结构体中匹配引用
如果你有一个包含引用的结构体,可以在 `match` 表达式中匹配这些引用:
```rust
struct Point {
x: i32,
y: i32,
}
fn main() {
let point = Point { x: 10, y: 20 };
let r = &point;
match r {
&Point { x, y } => println!("Point has coordinates: ({}, {})", x, y),
}
}
```
在这个例子中,我们匹配一个包含引用的结构体 `Point`。通过 `&Point { x, y }` 模式,我们可以解引用 `r` 并将其字段绑定到 `x` 和 `y`。
### 5. 在枚举中匹配引用
如果你有一个包含引用的枚举,可以在 `match` 表达式中匹配这些引用:
```rust
enum Message {
Hello { id: i32 },
}
fn main() {
let msg = Message::Hello { id: 5 };
let r = &msg;
match r {
&Message::Hello { id } => println!("Hello message with id: {}", id),
}
}
```
在这个例子中,我们匹配一个包含引用的枚举 `Message`。通过 `&Message::Hello { id }` 模式,我们可以解引用 `r` 并将其字段绑定到 `id`。
### 总结
在 Rust 中,`match` 表达式可以灵活地用于匹配引用类型。你可以使用 `&`、`ref` 和 `ref mut` 关键字来匹配和处理引用。无论是简单的引用,还是结构体和枚举中的引用,`match` 表达式都提供了强大的模式匹配能力,使得代码更加清晰和易于维护。
fn a(click: i32, current_hex:i32) -> Result<i32, Err>{
match point_to_hex(click) {
None => Err("That's not a game space."),
Some(hex) if hex == current_hex =>
Err("You are already there! You must click somewhereelse"),
Some(hex) => Ok(hex)
}
}
Some(hex) if hex == current_hex