我认为,一旦移动了一个对象,它在堆栈上占用的内存就可以用于其他目的.然而,下面的最小示例显示了相反的情况.

#[inline(never)]
fn consume_string(s: String) {
    drop(s);
}

fn main() {
    println!(
        "String occupies {} bytes on the stack.",
        std::mem::size_of::<String>()
    );

    let s = String::from("hello");
    println!("s at {:p}", &s);
    consume_string(s);

    let r = String::from("world");
    println!("r at {:p}", &r);
    consume_string(r);
}

编译带有--release标志的代码后,它在我的计算机上给出以下输出.

String occupies 24 bytes on the stack.
s at 0x7ffee3b011b0
r at 0x7ffee3b011c8

很明显,即使移动了sr也不会重用堆栈上最初属于s的24字节块.我认为重用移动对象的堆栈内存是安全的,但Rust编译器为什么不这样做呢?我是不是错过了什么角落的案子?

更新:

#[inline(never)]
fn consume_string(s: String) {
    drop(s);
}

fn main() {
    println!(
        "String occupies {} bytes on the stack.",
        std::mem::size_of::<String>()
    );

    {
        let s = String::from("hello");
        println!("s at {:p}", &s);
        consume_string(s);
    }

    let r = String::from("world");
    println!("r at {:p}", &r);
    consume_string(r);
}

上面的代码给出了下面的输出.

String occupies 24 bytes on the stack.
s at 0x7ffee2ca31f8
r at 0x7ffee2ca31f8

我认为花括号不应该有任何区别,因为s的生存期在调用comsume_string(s)后结束,其丢弃处理程序在comsume_string()内调用.为什么添加花括号可以实现优化?

下面给出了我使用的Rust编译器的版本.

rustc 1.54.0-nightly (5c0292654 2021-05-11)
binary: rustc
commit-hash: 5c029265465301fe9cb3960ce2a5da6c99b8dcf2
commit-date: 2021-05-11
host: x86_64-apple-darwin
release: 1.54.0-nightly
LLVM version: 12.0.1

更新2:

  1. 这是一个无效的优化.在某些情况下,如果我们执行"优化",编译的代码可能会失败.
  2. 这是一个有效的优化,但编译器(包括rustc前端和llvm)无法执行它.
  3. 这是一个有效的优化,但暂时关闭,如this.
  4. 这是一个有效的优化,但没有实现.它将在future 添加.

推荐答案

我的TLDR结论:错过了优化机会.

所以我做的第一件事就是研究你的consume_string函数是否真的起作用.为此,我创建了以下(稍微多一点)最小示例:

struct Obj([u8; 8]);
fn main()
{
    println!(
        "Obj occupies {} bytes on the stack.",
        std::mem::size_of::<Obj>()
    );

    let s = Obj([1,2,3,4,5,6,7,8]);
    println!("{:p}", &s);
    std::mem::drop(s);
    
    let r = Obj([11,12,13,14,15,16,17,18]);
    println!("{:p}", &r);
    std::mem::drop(r);
}

我用的不是consume_string而是std::mem::drop,它专门用来消费一件物品.此代码的行为与您的代码一样:

Obj occupies 8 bytes on the stack.
0x7ffe81a43fa0
0x7ffe81a43fa8

移除drop不会影响结果.

所以问题是,为什么rustc没有注意到在r通电之前s已经死了.如第二个示例所示,在一个范围中包含s将允许优化.

为什么会这样?因为Rust语义规定对象在其作用域的末尾被删除.由于s位于内部作用域中,因此在作用域退出之前会删除它.如果没有作用域,s将一直处于活动状态,直到main函数退出.

为什么当s移动到一个函数中时,它不起作用,它应该在退出时被删除?

Rust相关问答推荐

属性宏修改派生宏的派生实现

给定使用newype习语定义的类型上的铁 rust Vec,有没有方法获得底层原始类型的一部分?

无需通过ASIO输入音频,并使用cpal进行反馈示例

在Rust中,有没有一种方法让我定义两个 struct ,其中两个都遵循标准 struct ?

为什么我可以跟踪以前borrow 过的变量?房主在哪里?

默认特征实现中的生命周期问题

如何为 struct 字段设置新值并在Ruust中的可变方法中返回旧值

什么时候和为什么S最好是按值或引用传递简单类型

我应该将哪些文件放入我的GitHub存储库

将一个泛型类型转换为另一个泛型类型

获取与父字符串相关的&;str的原始片段

Const 上下文:从 init 函数创建具有 const 通用长度的数组

try 实现线程安全的缓存

如何在 Rust 中显式声明 std::str::Matches<'a, P> ?

分配给下划线模式时会发生什么?

在 Bevy 项目中为 TextureAtlas 精灵实施 NearestNeighbor 的正确方法是什么?

为什么 Rust 允许写入不可变的 RwLock?

将 `&T` 转换为新类型 `&N`

如何解析 Rust 中的 yaml 条件字段?

如何构建包含本地依赖项的 docker 镜像?