【问题标题】:How does exactly rust handle return valuesrust 如何准确处理返回值
【发布时间】:2018-03-27 10:27:36
【问题描述】:

我对我的代码有疑问:

pub fn get_signals(path: &String) -> Vec<Vec<f64>> {
    let mut rdr = csv::ReaderBuilder::new().delimiter(b';').from_path(&path).unwrap();

    let mut signals: Vec<Vec<f64>> = Vec::new();

    for record in rdr.records(){
        let mut r = record.unwrap();
        for (i, value) in r.iter().enumerate(){
            match signals.get(i){
                Some(_) => {},
                None    => signals.push(Vec::new())
            }
            signals[i].push(value.parse::<f64>().unwrap());
        }
    }

    signals
}

Rust 究竟是如何处理返回的?例如,当我写let signals = get_signal(&amp;"data.csv".to_string()); 时,Rust 是否假设我想要一个新的 Vec 实例(复制所有数据)或者只是将一个指针传递给以前分配的内存(通过Vec::new())?最有效的方法是什么?另外,rdr 会发生什么?我假设,鉴于 Rust 的内存安全,它已经被破坏了。

【问题讨论】:

  • 您好,欢迎您。如果你还没有,我建议你看看这本书 (here)。
  • 我认为命运rdr 是一个完全不同的问题。简而言之:是的,如果没有移动范围结束的任何值,它都会被销毁。这意味着调用他们对Drop::drop 的实现,如果有的话。而且这与内存安全无关。内存安全与泄漏(或不泄漏)内存和其他资源无关,它与避免悬空指针和双重释放有关。不破坏任何东西是避免悬空指针和双重释放的一种非常安全的方法;这是Clang编译器使用的策略,是Java Epsilon GC的策略
  • 所以,如果我错了,请纠正我。当最后一个指向内存的指针失效时,它会午餐对象 Drop::drop 实现,如果它存在(?)。那么如果内存在堆上,它会一直被占用吗?我可以稍后分配这个内存吗(是否可用)?
  • 我真的认为你应该在这里问一个单独的问题;或者检查机制的描述是否已经不可用。对于复杂的主题,评论并不是那么好。

标签: rust runtime


【解决方案1】:

Rust 究竟是如何处理返回的?

Rust 语言所做的唯一保证是,如果代码中没有明确的 .clone(),值永远不会克隆。因此,从语义的角度来看,该值是 moved 的,不需要分配内存。

Rust 是否假设我想要一个 Vec 的新实例(复制所有数据)或者只是传递一个指向先前分配的内存的指针(通过 Vec::new())?

这是特定于实现的,并且是 ABI(应用程序二进制接口)的一部分。 Rust ABI 没有正式化,并且不稳定,因此没有标准描述它,也无法保证这种支持。

此外,这将取决于函数调用是内联还是。如果函数调用是内联的,当然不会再有return,但应该观察到相同的行为。

对于小的值,它们应该通过一个寄存器(或几个寄存器)返回。

对于更大的值

  • 调用方应在堆栈上保留内存(大小和对齐方式正确)并将指向该区域的指针传递给被调用方,
  • 然后,被调用者将在指向的位置构造返回值,以便在它返回时该值已存在于该位置以供调用者使用。

注意:这里的 size 是堆栈上的大小,由std::mem::size_of 返回;所以size_of::&lt;Vec&lt;_&gt;&gt;() == 24 在 64 位架构上。

最有效的方法是什么?

返回与单次调用一样高效

但是,如果您发现自己处于一种情况,例如,您想逐行读取文件,那么重用缓冲区从一个调用到另一个调用是有意义的,这可以实现通过:

  • &amp;mut 引用到缓冲区(StringVec&lt;u8&gt; 说),
  • 或按值获取缓冲区并返回。

重点是避免内存分配。

【讨论】:

  • 感谢您的澄清。我想坚持一下我的情况。您说过大量数据应该预先分配在堆栈上。在我的例子中,函数在堆上动态分配内存。我当然知道可能有很多重新分配,但目前我可以忍受。我的问题是:返回数据的方式是特定于实现的吗?例如,在 Vecs 情况下,它返回 pointer, capacity, length。因此,此数据已移出函数范围。堆分配的内存保持在原来的位置。我无法想象是否有人会这样做。
  • @Kazik:好点,我添加了一个注释,这里感兴趣的大小是由size_of 返回的,所以你是对的,这里只复制了指针、容量和长度。不确定这些是通过寄存器还是堆栈。堆分配的数据确实保持不变,实际上,它甚至保持未触及:指针没有作为移动的一部分取消引用。
  • "如果没有明确的 .clone() 在代码中,值永远不会被克隆" - 除非它是一个 Copy 结构,它仍然与clone().
  • @BHustus:如果是复制,那么复制和移动在字节级别上是一回事,所以没关系 :) 另一方面,克隆可能会导致深度复制.. . 并且来自 C++ 隐式深拷贝是一种痛苦:(
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-09-10
  • 2022-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-18
相关资源
最近更新 更多