我想知道两者之间有什么区别:
"some string".to_string()
和
"some string".into_string()
前者似乎来自ToString,这很清楚。
但是,后者似乎来自IntoString,这对我来说不太清楚。
它对价值意味着什么consume
?这两个特征有什么区别?
在做了一些挖掘之后的附加信息。
这是for a的当前实现into_string
String
。正如你所看到的,它只返回自己,所以没有分配。
我想知道两者之间有什么区别:
"some string".to_string()
和
"some string".into_string()
前者似乎来自ToString,这很清楚。
但是,后者似乎来自IntoString,这对我来说不太清楚。
它对价值意味着什么consume
?这两个特征有什么区别?
在做了一些挖掘之后的附加信息。
这是for a的当前实现into_string
String
。正如你所看到的,它只返回自己,所以没有分配。
它对价值意味着什么
consume
?
消费一个值与移动一个值有关。在讨论这两个特征之间的区别之前,我将举例说明移动值的含义。让我们创建一个Vec
字符Ascii
:asciis
.
fn main() {
let asciis = vec!['h'.to_ascii(), 'i'.to_ascii()];
println!("{}", asciis);
}
在内部,Vec
是一个具有三个字段的结构:
Vec
。Vec
。Vec
.从图形上看,它的内存布局Vec
和它所管理的数据可能看起来像这样。
Stack: asciis Heap:
+----------+ +----------+
0xF0 | data | ----> 0xA0 | 'h' |
+----------+ +----------+
0xF4 | length | 0xA1 | 'i' |
+----------+ +----------+
0xF8 | capacity |
+----------+
当我们Vec
超出范围时,它会释放它正在管理的内存。释放的内存对我们来说是垃圾。访问释放的内存是错误的。这看起来像下面这样。消失了,堆上的Vec
内存已被释放。
Heap:
+----------+
0xA0 | GARBAGE |
+----------+
0xA1 | GARBAGE |
+----------+
现在,让我们回到我们的代码并尝试复制asciis
.
fn main() {
let asciis = vec!['h'.to_ascii(), 'i'.to_ascii()];
{
let an_attempted_copy = asciis;
}
println!("{}", asciis);
}
让我们猜测这an_attempted_copy
是asciis
. 制作完副本后,我们的记忆可能如下所示。
Stack: asciis Heap: Stack: an_attempted_copy
+----------+ +----------+ +----------+
0xF0 | data | ----> 0xA0 | 'h' | <---- 0xE0 | data |
+----------+ +----------+ +----------+
0xF4 | length | 0xA1 | 'i' | | length |
+----------+ +----------+ +----------+
0xF8 | capacity | | capacity |
+----------+ +----------+
就在我们尝试之前println!
asciis
,an_attempted_copy
超出范围!和之前一样,我们指向的数据Vec
被释放了。
Stack: asciis Heap:
+----------+ +----------+
0xF0 | data | ----> 0xA0 | GARBAGE |
+----------+ +----------+
0xF4 | length | 0xA1 | GARBAGE |
+----------+ +----------+
0xF8 | capacity |
+----------+
哦哦,asciis
是指向被释放的内存!这是个坏消息,因为我们正要这样做println!
asciis
。
那么我们将如何补救这种情况呢?好吧,这里有两个选择。
asciis
到an_attempted_copy
时,我们可以将 指向的数据复制asciis
到一个新分配的内存中。其他语言(如 C++)执行此操作。asciis
!这就是生锈的作用。那么搬家是什么意思呢?这意味着an_attempted_copy
它将拥有先前指向的数据的所有权asciis
。asciis
失去所有权,我们不能再使用它了。为了清楚起见,让我们重命名an_attempted_copy
。
fn main() {
let asciis = vec!['h'.to_ascii(), 'i'.to_ascii()];
{
let actually_a_move = asciis;
}
println!("{}", asciis);
}
现在,让我们在进入actually_a_move
.
Stack: asciis Heap: Stack: actually_a_move
+----------+ +----------+ +----------+
0xF0 | GARBAGE | 0xA0 | 'h' | <---- 0xE0 | data |
+----------+ +----------+ +----------+
0xF4 | GARBAGE | 0xA1 | 'i' | | length |
+----------+ +----------+ +----------+
0xF8 | GARBAGE | | capacity |
+----------+ +----------+
asciis
不再拥有内存,所以我们不能再使用asciis
了。这意味着它几乎是垃圾。那么如果我们不能再使用asciis
了,当我们使用println!
它时会发生什么?我们得到以下错误。
<anon>:6:24: 6:30 error: use of moved value: `asciis`
<anon>:6 println!("{}", asciis);
^~~~~~
note: in expansion of format_args!
<std macros>:2:23: 2:77 note: expansion site
<std macros>:1:1: 3:2 note: in expansion of println!
<anon>:6:9: 6:32 note: expansion site
<anon>:4:17: 4:32 note: `asciis` moved here because it has type `collections::vec::Vec<std::ascii::Ascii>`, which is moved by default (use `ref` to override)
<anon>:4 let actually_a_move = asciis;
^~~~~~~~~~~~~~~
error: aborting due to previous error
正如预期的那样,rust 编译器告诉我们我们正在尝试使用ascii
,但它ascii
是一个移动的值;这是错误的。
移动语义(以及诸如借用和生命周期等相关主题)是困难的东西。我在这里只触及了表面。有关更多信息,rust by example和这个 stackoverflow question 都是很好的资源。
to_string
对比into_string
这两个特征有什么区别?
现在我已经探索了消费或移动值的概念,让我们来看看这两个特征之间的区别。我们先来看看 的类型签名to_string
。
fn to_string(&self) -> String;
这个函数接受一个引用self
并返回一个新String
的供我们使用。我没有讨论参考资料以及它们如何影响运动,但是当我说这里没有做任何运动时请相信我。
现在让我们看看into_string
.
fn into_string(self) -> String;
此函数不引用self
. 相反,self
被移动到函数中。
那么这种差异的含义是什么?让我们看一个例子。
fn main() {
let asciis = vec!['h'.to_ascii(), 'i'.to_ascii()];
let no_moves_here = asciis.to_string();
println!("{}", asciis);
}
我们再次创建一个Vec
角色Ascii
。然后,当我们调用 时,会创建asciis.to_string()
一个全新的并且永远不会移动。此代码将按照您的预期构建和运行,打印出. 现在,让我们使用.String
asciis
[h, i]
into_string
fn main() {
let asciis = vec!['h'.to_ascii(), 'i'.to_ascii()];
let uh_oh_we_just_moved_asciis = asciis.into_string();
println!("{}", asciis);
}
这是我们在尝试构建此代码时收到的错误消息。
<anon>:4:24: 4:30 error: use of moved value: `asciis`
<anon>:4 println!("{}", asciis);
^~~~~~
note: in expansion of format_args!
<std macros>:2:23: 2:77 note: expansion site
<std macros>:1:1: 3:2 note: in expansion of println!
<anon>:4:9: 4:32 note: expansion site
<anon>:3:42: 3:48 note: `asciis` moved here because it has type `collections::vec::Vec<std::ascii::Ascii>`, which is non-copyable (perhaps you meant to use clone()?)
<anon>:3 let uh_oh_we_just_moved_asciis = asciis.into_string();
^~~~~~
error: aborting due to previous error
所以发生了什么事?Wellasciis
正在被移入函数into_string
中。就像我们上次asciis
在移动它之后尝试使用一样,rust 编译器会拒绝我们的代码。
这是对“移动语义”的引用,诚然基本上没有文档。对于那个很抱歉!不同之处在于,如果一个值移动,您将无法再使用它。换句话说,这有效:
fn main() {
let x = "hello".to_string();
let y = x.to_string();
let z = x.into_string();
}
但是这个错误:
fn main() {
let x = "hello".to_string();
let z = x.into_string();
let y = x.to_string();
}
和
<anon>:5:13: 5:14 error: use of moved value: `x`
<anon>:5 let y = x.to_string();
^
<anon>:3:17: 3:18 note: `x` moved here because it has type `collections::string::String`, which is non-copyable (perhaps you meant to use clone()?)
<anon>:3 let z = x.into_string();
^
那有意义吗?