支持将 ContiguousBytes 转换为 Array 和 "not-Array" 的现代规范方法是什么?

What is the modern canonical way to support converting ContiguousBytes into both Array and "not-Array"?

例如,如果没有第二次重载,加载数组将产生“UnsafeRawBufferPointer.load 越界”。有没有办法在不重载的情况下处理这两种情况?

let bytes: [UInt8] = [1, 0, 1, 0]
bytes.load() as Int32 // 0x1_00_01
bytes.load() as [Int16] // [1, 1]
public extension ContiguousBytes {
  func load<T>(_: T.Type = T.self) -> T {
    withUnsafeBytes { [=12=].load(as: T.self) }
  }

  func load<Element>(_: [Element].Type = [Element].self) -> [Element] {
    withUnsafeBytes { .init([=12=].bindMemory(to: Element.self)) }
  }
}

关于有两个重载:很遗憾,您无法避免它们。 ContiguousBytes.withUnsafeBytes 使您可以访问实现 ContiguousBytes 的类型的底层 存储 ,因此 [UInt8].withUnsafeBytes 给出的缓冲区将是实际的数组实例用于 store 数据的缓冲区,而不是内存中指向 自身 的指针(例如它的长度、容量、存储指针等) .

您的第一个重载无法工作的原因是您将调用等效于:

withUnsafeBytes { rawBuffer in
    rawBuffer.load(as: [Int16].self)
}

其中 load(as:) 试图读取 [1, 0, 1, 0] 就好像这些内容是单个 [Int16] 实例的长度、容量和缓冲区指针一样,但事实并非如此 — 它是只有 bytes.

内容

您需要第二次重载才能创建一个包含 UnsafeRawBufferPointer.

内容 的新数组

至于数组变体的实际实现,从 Swift 5.7 中的 SE-0333 Expand usability of withMemoryRebound 开始,正确的方法是使用 withMemoryRebound(to:_:),因为无条件如果缓冲区已经绑定到不同的类型,bindMemory(to:) 是不安全的。 withMemoryRebound(to:_:) 现在总是安全的,即使缓冲区已绑定,只要您满足对齐要求:

/// A raw buffer may represent memory that has been bound to a type.
/// If that is the case, then T must be layout compatible with the
/// type to which the memory has been bound. This requirement does not
/// apply if the raw buffer represents memory that has not been bound
/// to any type.

这看起来像

func load<Element>(_: [Element].Type = [Element].self) -> [Element] {
    withUnsafeBytes { rawBuffer in
        rawBuffer.withMemoryRebound(to: Element.self) { typedBuffer in
            Array(typedBuffer)
        }
    }
}

UnsafeRawBufferPointer.withMemoryRebound(to:_:) 将计算将原始字节绑定到 Element 的右步长,但请记住,这 仅对 有效如果原始缓冲区的对齐方式符合 Element 的对齐要求,则执行此操作 - 否则您将崩溃或触发未定义的行为。