ToString和IntoString之间的区别

con*_*com 10 rust

我想知道之间的区别是什么:

"some string".to_string()
Run Code Online (Sandbox Code Playgroud)

"some string".into_string()
Run Code Online (Sandbox Code Playgroud)

前者似乎来自ToString,这很清楚.

然而,后者似乎来自IntoString,这对我来说不太清楚.

这对consume价值意味着什么?这两个特质有什么区别?


在做了一些挖掘之后的其他信息.

下面是目前执行into_stringString.如您所见,它只返回自身,因此不进行任何分配.

mwh*_*ker 25

移动语义

这对consume价值意味着什么?

消耗值与移动值有关.在我讨论这两个特征之间的差异之前,我将举一些例子来说明移动一个值的意义.让我们创建一个VecAscii字符:asciis.

fn main() {
    let asciis = vec!['h'.to_ascii(), 'i'.to_ascii()];
    println!("{}", asciis);
}
Run Code Online (Sandbox Code Playgroud)

在内部,这Vec是一个包含三个字段的结构:

  1. 的长度Vec.
  2. 的能力Vec.
  3. 指向由...管理的数据的指针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.

那么我们如何纠正这种情况呢?嗯,这里有两个选择.

  1. 当我们复制asciis到时an_attempted_copy,我们可以将指向的数据复制asciis到新分配的内存中.其他语言如C++就是这样做的.
  2. 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)

我们再次创造VecAscii人物.然后,当我们打电话时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移动后尝试使用的一样,生锈编译器将拒绝我们的代码.

  • 这是一个很好的答案,但我对最后一部分仍有疑问。这是否意味着`to_string`,因为它只是临时借用,必须在内存中进行复制才能返回一个字符串,而`into_string`因为它消耗了我们的字符串可以返回指向内存中相同字节的不同类型? (2认同)