我想知道之间的区别是什么:
"some string".to_string()
Run Code Online (Sandbox Code Playgroud)
和
"some string".into_string()
Run Code Online (Sandbox Code Playgroud)
前者似乎来自ToString,这很清楚.
然而,后者似乎来自IntoString,这对我来说不太清楚.
这对consume价值意味着什么?这两个特质有什么区别?
在做了一些挖掘之后的其他信息.
下面是目前执行into_string的String.如您所见,它只返回自身,因此不进行任何分配.
mwh*_*ker 25
这对
consume价值意味着什么?
消耗值与移动值有关.在我讨论这两个特征之间的差异之前,我将举一些例子来说明移动一个值的意义.让我们创建一个Vec的Ascii字符:asciis.
fn main() {
let asciis = vec!['h'.to_ascii(), 'i'.to_ascii()];
println!("{}", asciis);
}
Run Code Online (Sandbox Code Playgroud)
在内部,这Vec是一个包含三个字段的结构:
Vec.Vec.Vec.从图形Vec上看,它所管理的数据的内存布局可能看起来像这样.
Stack: asciis Heap:
+----------+ +----------+
0xF0 | data | ----> 0xA0 | 'h' |
+----------+ +----------+
0xF4 | length | 0xA1 | 'i' |
+----------+ +----------+
0xF8 | capacity |
+----------+
Run Code Online (Sandbox Code Playgroud)
当我们Vec超出范围时,它释放了它正在管理的内存.释放的记忆对我们来说是垃圾.访问释放的内存是错误的.这看起来像下面这样.它Vec已经消失,堆上的内存已被释放.
Heap:
+----------+
0xA0 | GARBAGE |
+----------+
0xA1 | GARBAGE |
+----------+
Run Code Online (Sandbox Code Playgroud)
现在,让我们回到我们的代码并尝试制作副本asciis.
fn main() {
let asciis = vec!['h'.to_ascii(), 'i'.to_ascii()];
{
let an_attempted_copy = asciis;
}
println!("{}", asciis);
}
Run Code Online (Sandbox Code Playgroud)
我们猜测这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 |
+----------+ +----------+
Run Code Online (Sandbox Code Playgroud)
在我们尝试之前println! asciis,an_attempted_copy超出范围!就像以前一样,我们指向的数据Vec被释放了.
Stack: asciis Heap:
+----------+ +----------+
0xF0 | data | ----> 0xA0 | GARBAGE |
+----------+ +----------+
0xF4 | length | 0xA1 | GARBAGE |
+----------+ +----------+
0xF8 | capacity |
+----------+
Run Code Online (Sandbox Code Playgroud)
哦,哦,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);
}
Run Code Online (Sandbox Code Playgroud)
现在,让我们在进入后立即绘制内存布局actually_a_move.
Stack: asciis Heap: Stack: actually_a_move
+----------+ +----------+ +----------+
0xF0 | GARBAGE | 0xA0 | 'h' | <---- 0xE0 | data |
+----------+ +----------+ +----------+
0xF4 | GARBAGE | 0xA1 | 'i' | | length |
+----------+ +----------+ +----------+
0xF8 | GARBAGE | | capacity |
+----------+ +----------+
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
正如预期的那样,生锈编译器告诉我们我们正在尝试使用ascii,但它ascii是一个移动的值; 这是错误的.
移动语义(以及借用和生命周期等相关主题)是很难的.我在这里只是勉强抓住了表面.有关更多信息,示例生锈和此stackoverflow问题都是很好的资源.
to_string VS into_string这两个特质有什么区别?
现在我已经探索了消费或移动价值的概念,让我们来看看这两个特征之间的差异.我们先来看看它的类型签名to_string.
fn to_string(&self) -> String;
Run Code Online (Sandbox Code Playgroud)
这个函数引用self并返回一个新的String供我们使用.我没有讨论参考文献以及它们如何影响运动,但是当我说这里没有动作时,请相信我.
现在让我们来看看类型签名into_string.
fn into_string(self) -> String;
Run Code Online (Sandbox Code Playgroud)
此功能不参考self.相反,self移入功能.
那么这种差异的含义是什么呢?我们来看一个例子吧.
fn main() {
let asciis = vec!['h'.to_ascii(), 'i'.to_ascii()];
let no_moves_here = asciis.to_string();
println!("{}", asciis);
}
Run Code Online (Sandbox Code Playgroud)
我们再次创造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);
}
Run Code Online (Sandbox Code Playgroud)
这是我们在尝试构建此代码时获得的错误消息.
<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
Run Code Online (Sandbox Code Playgroud)
所以发生了什么事?嗯asciis正在进入该功能into_string.就像我们上次asciis移动后尝试使用的一样,生锈编译器将拒绝我们的代码.