Rust 与 C++:从函数返回对象

Ash*_*iya 2 c++ rust

我是 Rust 新手,试图了解从函数返回对象时如何传递所有权。在以下基于引用的实现中,由于引用没有所有权,因此当“s”超出范围时,它会被删除并释放。

fn dangle() -> &String { // dangle returns a reference to a String

    let s = String::from("hello"); // s is a new String

    &s // we return a reference to the String, s
} // Here, s goes out of scope, and is dropped. Its memory goes away.
  // Danger!
Run Code Online (Sandbox Code Playgroud)

这是通过不返回引用来解决的:

fn no_dangle() -> String {
    let s = String::from("hello");

    s
}
Run Code Online (Sandbox Code Playgroud)

现在我尝试用 C++ 实现来理解这一点,如下所示:

std::string no_dangle() {
    std::string s("hello world");
    return s;
}
Run Code Online (Sandbox Code Playgroud)

根据我的理解,在 C++ 中,当从函数返回“s”时,使用复制构造函数创建另一个副本,并且在函数内部创建的“s”被释放。这意味着,创建了两个实际上不是光学的对象记忆方面。

我的问题:

  1. 在 Rust 中,当从函数返回“s”时,不会创建额外的对象。仅返回所有权。在堆中分配的原始对象保持不变。这是正确的吗?

  2. 在 C++ 中,你可以通过返回对象和指针(智能指针或原始指针)来从函数返回“事物”。但是在 Rust 中,唯一返回“事物”的就是上面的内容,与 C++ 相比,它接近于返回一个智能指针。 -指针?

use*_*198 6

Rust 和 C++ 都是值类型语言,因此除非明确要求,否则不会在堆上分配对象/结构。因此,在给出的两种情况下,都没有在堆上分配有问题的字符串对象/结构。在这两种语言中,字符串都使用存储在堆上的动态分配的后备缓冲区,但这是一个重要的区别。

因此,在 Rust 中,如果按值返回,则对象将被移动,这始终相当于直接 memcpy,因为 Rust 结构不允许具有自定义移动逻辑,并且克隆必须是显式的。memcopy 将指针复制到后备存储,因此字符串对象可能位于不同的内存中,但后备缓冲区保持不变。

在 C++ 中,对象可以具有重要的复制和(在 C++11 及更高版本中)移动构造函数。因此,如果这不是返回命名值,则必须调用复制或移动构造函数。然而,对于从函数返回的特定情况,复制省略规则开始发挥作用。这表示,可选地(在 C++17 及更高版本中,某些简单情况需要),如果对象在 return 语句中初始化,或者来自具有自动存储持续时间的位置,则编译器不会调用复制/移动构造函数,而是将对象直接构造到调用者在最初创建返回对象时提供的存储中,这意味着在返回时不需要复制或移动。这称为返回值优化。

如果在 C++11 或更高版本中,您要返回的值不是对象初始化或具有自动存储持续时间的命名值(或者在这些情况下,由编译器自行决定,C++17 及更高版本中的对象初始化除外),例如作为调用另一个函数的结果,那么将调用移动构造函数,在这种情况下,只需将指针复制到后备存储并清除旧字符串中的指针。在这种情况下,行为就像生锈一样。如果该类型具有更复杂的移动构造函数,则它可以因移动而执行任何操作。

最后,在 C++98 中,如果要返回的值不是对象初始化或具有自动存储持续时间的命名值,则将调用复制构造函数,将后备存储复制到新的后备存储,然后将该后备存储复制到新的后备存储中。回。导致新的字符串指向不同的内存。当作用域结束时,旧的内存将被析构函数释放。

另外,C++ 实现可以使用小字符串优化,其中小字符串直接存储在字符串对象中。在这种情况下,将没有后备存储,并且即使移动对象,也必须复制字符串。

最后需要注意的一点是,在 C++11 之前,实现通常std::string使用引用计数后备存储。在这种情况下,副本将增加后备存储上的引用计数,而析构函数将减少其增量,但不会释放,因为仍然存在对存储的引用。在这种情况下,生成的字符串仍将指向原始后备存储,但代价是比移动过程稍微昂贵一些。随着移动构造函数的引入,这种情况已经变得不那么常见了。

为了快速回答第二个问题,rust 还允许返回智能指针、指针和引用,但是 rust 借用检查器将阻止返回对对象本地对象的引用,因为它们没有足够的生命周期。这不会阻止返回对参数和全局变量(例如字符串文字或线程局部变量)的引用,因为它们的生命周期比函数更长。