Poor ergonomics of Array.init(unsafeUninitializedCapacity:initializingWith:)

i will admit 99% of the time i use Array.init(unsafeUninitializedCapacity:initializingWith:), i completely ignore memory management and i just assign through UnsafeMutableBufferPointer.subscript(_:) because the element type is trivial.

let _:[UInt8] = .init(unsafeUninitializedCapacity: elements.count)
{
    for (i, value):(Int, UInt8) in elements
    {
        $0[i] = value
    }
    $1 = elements.count
}

but if the element type is not trivial, then you actually have to initialize instead of assign, and UnsafeMutableBufferPointer doesn’t have an API to initialize a single element of the buffer at a particular index, instead you have to optionally unwrap the baseAddress and perform pointer arithmetic:

let _:[C] = .init(unsafeUninitializedCapacity: elements.count)
{
    guard let start:UnsafeMutablePointer<C> = $0.baseAddress
    else
    {
        $1 = 0
        return
    }
    for (i, value):(Int, C) in elements
    {
        (start + i).initialize(to: value)
    }
    $1 = elements.count
}

UnsafeMutableBufferPointer<T>.initializeElement(at: Int, to: T)?

3 Likes

aha, that’s exactly what i needed! is it new in 5.8?

It looks like it was added 10 months ago, but it is marked @_alwaysEmitIntoClient so as long as you have a new enough stdlib to compile against you should have it and be able to use it as far back as you want.

5 Likes