汕头市网站建设分站公司,wordpress调用自定义文章类型文章,做私房蛋糕在哪些网站写东西,wordpress的固定链接如何设置1.变量与数据交互方式之二: 克隆
在上一节中, 我们讨论了变量与数据交互的第一种方式: 移动, 本节将介绍第二种方式:克隆。
如果我们 确实 需要深度复制 String 中堆上的数据#xff0c;而不仅仅是栈上的数据#xff0c;可以使用一个叫做 clone 的通用函数。
看下面的代码…1.变量与数据交互方式之二: 克隆
在上一节中, 我们讨论了变量与数据交互的第一种方式: 移动, 本节将介绍第二种方式:克隆。
如果我们 确实 需要深度复制 String 中堆上的数据而不仅仅是栈上的数据可以使用一个叫做 clone 的通用函数。
看下面的代码:
let s1 String::from(hello);
let s2 s1.clone();
println!(s1 {}, s2 {}, s1, s2);
这段代码能正常运行, 并且堆上的数据现在可以被复制了。
我们在代码中下个断点, 使用调试器观察下s1的内容,如图: 结合上一章节的分析, 此时s1变量中保存的指针ptr指向的内存保存了内容:hello。
现在执行语句: let s2 s1.clone(); 单不执行一下看下s2的内容,如图: 可以看到, clone()函数的确将字符串内容复制到变量s2中。
注意:当出现 clone 调用时我们心里要清楚一些特定的代码被执行而且这些代码可能相当消耗资源。很容易能察觉到一些不寻常的事情正在发生。
下面再看一段代码:
let x 5;
let y x;
println!(x {}, y {}, x, y);
执行这段代码, 结果如下: 这段代码似乎与我们刚刚学到的内容相矛盾没有调用 clone不过 x 依然有效且没有被移动到 y 中。
原因是像整型这样的在编译时已知大小的类型被整个存储在栈上所以拷贝其实际的值是快速的。这意味着没有理由在创建变量 y 后使 x 无效。换句话说这里没有深浅拷贝的区别所以这里调用 clone 并不会与通常的浅拷贝有什么不同我们可以不用管它。
Rust 有一个叫做 Copy trait 的特殊注解可以用在类似整型这样的存储在栈上的类型上, 如果一个类型实现了 Copy trait那么一个旧的变量在将其赋值给其他变量后仍然可用。
Rust 不允许自身或其任何部分实现了 Drop trait 的类型使用 Copy trait。如果我们对其值离开作用域时需要特殊处理的类型使用 Copy 注解将会出现一个编译时错误。
那么哪些类型实现了 Copy trait 呢可以查看给定类型的文档来确认不过作为一个通用的规则任何一组简单标量值的组合都可以实现 Copy任何不需要分配内存或某种形式资源的类型都可以实现 Copy 。如下是一些 Copy 的类型 所有整数类型比如 u32。 布尔类型bool它的值是 true 和 false。 所有浮点数类型比如 f64。 字符类型char。 元组当且仅当其包含的类型也都实现 Copy 的时候。比如(i32, i32) 实现了 Copy但 (i32, String) 就没有。
2.所有权和函数
将值传递给函数与给变量赋值的原理相似。向函数传递值可能会移动或者复制就像赋值语句一样。
看一下下面的代码:
fn main() {let s String::from(hello); // s 进入作用域
takes_ownership(s); // s 的值移动到函数里 ...// ... 所以到这里不再有效
let x 5; // x 进入作用域
makes_copy(x); // x 应该移动函数里// 但 i32 是 Copy 的// 所以在后面可继续使用 x
} // 这里x 先移出了作用域然后是 s。但因为 s 的值已被移走// 没有特殊之处
fn takes_ownership(some_string: String) { // some_string 进入作用域println!({}, some_string);
} // 这里some_string 移出作用域并调用 drop 方法。// 占用的内存被释放
fn makes_copy(some_integer: i32) { // some_integer 进入作用域println!({}, some_integer);
} // 这里some_integer 移出作用域。没有特殊之处
当尝试在调用 takes_ownership 后使用 s 时Rust 会抛出一个编译时错误。这些静态检查使我们免于犯错。
3.返回值与作用域
返回值也可以转移所有权, 看下面的代码:
fn main() {let s1 gives_ownership(); // gives_ownership 将返回值// 转移给 s1
let s2 String::from(hello); // s2 进入作用域
let s3 takes_and_gives_back(s2); // s2 被移动到// takes_and_gives_back 中// 它也将返回值移给 s3
} // 这里s3 移出作用域并被丢弃。s2 也移出作用域但已被移走// 所以什么也不会发生。s1 离开作用域并被丢弃
fn gives_ownership() - String { // gives_ownership 会将// 返回值移动给// 调用它的函数
let some_string String::from(yours); // some_string 进入作用域。
some_string // 返回 some_string // 并移出给调用的函数//
}
// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) - String { // a_string 进入作用域//
a_string // 返回 a_string 并移出给调用的函数
}
变量的所有权总是遵循相同的模式将值赋给另一个变量时移动它。当持有堆中数据值的变量离开作用域时其值将通过 drop 被清理掉除非数据被移动为另一个变量所有。
虽然这样是可以的但是在每一个函数中都获取所有权并接着返回所有权有些啰嗦。如果我们想要函数使用一个值但不获取所有权该怎么办呢如果我们还要接着使用它的话每次都传进去再返回来就有点烦人了除此之外我们也可能想返回函数体中产生的一些数据。
我们可以使用元组来返回多个值, 看下面的代码:
fn main() {let s1 String::from(hello);
let (s2, len) calculate_length(s1);
println!(The length of {} is {}., s2, len);
}
fn calculate_length(s: String) - (String, usize) {let length s.len(); // len() 返回字符串的长度
(s, length)
}
但是这未免有些形式主义而且这种场景应该很常见。幸运的是Rust 对此提供了一个不用获取所有权就可以使用值的功能叫做 引用references。
咱们下一章将揭开引用与借用的神秘面纱。