13

我想知道两者之间有什么区别:

"some string".to_string()

"some string".into_string()

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

但是,后者似乎来自IntoString,这对我来说不太清楚。

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


在做了一些挖掘之后的附加信息。

这是for a的当前实现into_stringString。正如你所看到的,它只返回自己,所以没有分配。

4

2 回答 2

32

移动语义

它对价值意味着什么consume

消费一个值与移动一个值有关。在讨论这两个特征之间的区别之前,我将举例说明移动值的含义。让我们创建一个Vec字符Asciiasciis.

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

在内部Vec是一个具有三个字段的结构:

  1. 的长度Vec
  2. 的容量Vec
  3. 指向由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_copyasciis. 制作完副本后,我们的记忆可能如下所示。

Stack: asciis           Heap:                   Stack: an_attempted_copy
     +----------+            +----------+            +----------+
0xF0 | data     | ----> 0xA0 | 'h'      | <---- 0xE0 | data     |
     +----------+            +----------+            +----------+
0xF4 | length   |       0xA1 | 'i'      |            | length   |
     +----------+            +----------+            +----------+
0xF8 | capacity |                                    | capacity |
     +----------+                                    +----------+

就在我们尝试之前println! asciisan_attempted_copy超出范围!和之前一样,我们指向的数据Vec被释放了。

Stack: asciis           Heap:                   
     +----------+            +----------+            
0xF0 | data     | ----> 0xA0 | GARBAGE  | 
     +----------+            +----------+           
0xF4 | length   |       0xA1 | GARBAGE  |           
     +----------+            +----------+           
0xF8 | capacity |                                    
     +----------+                                   

哦哦,asciis是指向被释放的内存!这是个坏消息,因为我们正要这样做println! asciis

那么我们将如何补救这种情况呢?好吧,这里有两个选择。

  1. 当我们复制asciisan_attempted_copy时,我们可以将 指向的数据复制asciis到一个新分配的内存中。其他语言(如 C++)执行此操作。
  2. 我们可以移动它,而不是复制asciis!这就是生锈的作用。

那么搬家是什么意思呢?这意味着an_attempted_copy它将拥有先前指向的数据的所有权asciisasciis失去所有权,我们不能再使用它了。为了清楚起见,让我们重命名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()一个全新的并且永远不会移动。此代码将按照您的预期构建和运行,打印出. 现在,让我们使用.Stringasciis[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 编译器会拒绝我们的代码。

于 2014-08-14T21:29:45.967 回答
3

这是对“移动语义”的引用,诚然基本上没有文档。对于那个很抱歉!不同之处在于,如果一个值移动,您将无法再使用它。换句话说,这有效:

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();
                         ^

那有意义吗?

于 2014-08-14T20:55:34.483 回答