我在玩零大小类型(zst)时遇到了一个有趣的例子.对空数组的引用将转化为具有任何生存期的引用:

fn mold_slice<'a, T>(_: &'a T) -> &'a [T] {
    &[]
}

我想了一下这是怎么可能的,因为这里的"值"基本上存在于函数的堆栈框架上,但签名promise 返回一个对具有更长生存期的值的引用('a包含函数调用).我得出的结论是,这是因为空数组[]是一个基本上只静态存在的ZST.编译器可以"伪造"引用引用的值.

所以我try 了这个:

fn mold_unit<'a, T>(_: &'a T) -> &'a () {
    &()
}

然后编译器抱怨:

error: borrowed value does not live long enough
 --> <anon>:7:6
  |
7 |     &()
  |      ^^ temporary value created here
8 | }
  | - temporary value only lives until here
  |
note: borrowed value must be valid for the lifetime 'a as defined on the block at 6:40...
 --> <anon>:6:41
  |
6 | fn mold_unit<'a, T>(_: &'a T) -> &'a () {
  |                                         ^

它不适用于单元()类型,也不适用于空 struct :

struct Empty;

// fails to compile as well
fn mold_struct<'a, T>(_: &'a T) -> &'a Empty {
    &Empty
}

不知何故,单元类型和空 struct 的处理方式与空数组不同.除了作为ZST,这些值之间还有其他区别吗?这些差异(&[]适合任何生命周期 ,&()&Empty不适合)与ZST完全无关吗?

Playground example

推荐答案

并不是说[]是零大小的(尽管它是),而是[]是一个常量,编译时文本.这意味着编译器可以将其存储在可执行文件中,而不必在堆或堆栈上动态分配.这反过来意味着指向它的指针可以持续多长时间,因为可执行文件中的数据不会go 任何地方.

令人恼火的是,这并没有延伸到&[0],因为Rust不够聪明,无法意识到[0]definitely常数.您可以使用以下方法解决此问题:

fn mold_slice<'a, T>(_: &'a T) -> &'a [i32] {
    const C: &'static [i32] = &[0];
    C
}

这个技巧也适用于anything,你可以把它放入const,比如()Empty.

然而,现实地说,只使用这样的函数返回&'static次借阅会更简单,因为这可以自动强制为任何other次借阅.

Edit:之前的版本指出,&[]不是零尺寸,这有点相切.

Rust相关问答推荐

如何处理对打包字段的引用是未对齐错误?

将内部类型作为参数的泛型 struct 上的方法

我怎样才能从一个Rust 的日期中go 掉3年?

MacOS(AARCH64)上Ghidra中的二进制补丁导致进程终止

Rust:跨多个线程使用hashmap Arc和rwlock

这种获取-释放关系是如何运作的?

为潜在的下游实现使用泛型绑定而不是没有泛型绑定的trait

有没有办法避免在While循环中多次borrow `*分支`

S,一般性状和联想型性状有什么不同?

当对VEC;U8>;使用serde_json时,Base64编码是保护空间的好方法吗?

使用关联类型重写时特征的实现冲突

习语选项<;T>;到选项<;U>;当T->;U用From定义

可选包装枚举的反序列化

通过写入 std::io::stdout() 输出不可见

通过mem::transmute将数组展平安全吗?

为什么这段 Rust 代码会在没有递归或循环的情况下导致堆栈溢出?

从嵌入式 Rust 中的某个时刻开始经过的时间

如何将参数传递给Rust 的线程?

当 T 不是副本时,为什么取消引用 Box 不会抱怨移出共享引用?

预期的整数,找到 `&{integer}`