返回值是被移动的还是没有?

7
使用以下代码:
struct Point {
    x: f64,
    y: f64,
}

struct Rectangle {
    p1: Point,
    p2: Point,
}

impl Rectangle {
    pub fn new(x1: f64, y1: f64, x2: f64, y2: f64) -> Rectangle {
        let r = Rectangle {
            p1: Point { x: x1, y: y1 },
            p2: Point { x: x2, y: y2 },
        };
        // some code where r is used
        r
    }
}

let rectangle = Rectangle::new(0.0, 0.0, 10.0, 10.0);

从内存角度来看,rectangler是同一个实例还是r的副本?

我是否必须显式返回引用(类似于&r)?

我需要创建数百万个矩形,而我不希望有无用的副本。

1个回答

23
从内存角度来看,rectangle是与r相同实例还是r的副本?
未指定。
Rust语言规定了语言的语义,虽然它们在一定程度上限制了实现,但在这种情况下并不是这样。返回值如何通过调用堆栈传递是ABI的一部分,而且ABI不稳定(在Rust中),也是特定于平台的。
我是否必须显式返回引用(例如&r)? 不可能返回引用
你可以返回一个Box<Rectangle>,但内存分配的成本将超过首先复制Rectangle的成本,因此这几乎不可取。
您可以使用输出参数来强制执行此操作,但这会带来其他问题:
- 如果您有一个&mut Rectangle参数,则需要先拥有一个有效实例,这个实例必须被初始化;非常浪费。 - 如果您有一个指向未初始化内存的*mut Rectangle,则需要使用unsafe代码,这几乎无法满足。
然而...
我必须创建数百万个矩形,我不希望有无用的副本。 我认为您正在为此担忧。 性能调优的第一条规则是先测量;我怀疑你甚至无法在创建这些数百万个矩形时观察到性能问题。
编译器有多种技巧,例如:
  • 一开始甚至不会实例化rectangle实例,而是通过CPU寄存器传递其组件,
  • 在调用点内联new,避免任何复制,
  • ...
因此,在担心复制4个f64的成本之前,我会实现简单的解决方案,以发布模式进行编译,并观察发生了什么。

当发生“事实上”的复制时,Rust是否保证调用drop()来释放原始值的资源? - Ilya Loskutov
1
@Mergasov:我认为你的心理模型有误。在Rust中,有两个操作,分别由两个特征标识:CloneCopy。当您创建一个clone时,(用户)指定了创建克隆所涉及的逻辑;例如,对于String,这意味着分配第二个缓冲区并将第一个缓冲区的内容复制到其中。克隆后,两个实例将独立地生活。如果您不需要键入.clone(),则Rust只是_移动_实例。如果实例已经被移动,则它不会被丢弃。如果实例被复制(Copy),则删除是无操作的。 - Matthieu M.
1
@Mergasov:因此,_反向_是有保证的。Rust 保证当它 位拷贝 一个值(无论是移动还是 Copy)时,永远不会调用 Drop - Matthieu M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接