Is there no default(T) in Swift?

前端 未结 3 2304
感情败类
感情败类 2021-02-18 22:40

I\'m trying to port the Matrix example from Swift book to be generic.

Here\'s what I got so far:

struct Matrix {
    let rows: Int, columns: Int         


        
3条回答
  •  没有蜡笔的小新
    2021-02-18 23:32

    There is a way to get the equivalent of default(T) in swift, but it's not free and it has an associated hazard:

    public func defaultValue() -> T {
        let ptr = UnsafeMutablePointer.alloc(1)
        let retval = ptr.memory
        ptr.dealloc(1)
        return retval;
    }
    

    Now this is clearly a hack because we don't know if alloc() initializes to something knowable. Is it all 0's? Stuff left over in the heap? Who knows? Furthermore, what it is today could be something different tomorrow.

    In fact, using the return value for anything other than a placeholder is dangerous. Let's say that you have code like this:

    public class Foo { /* implementation */
    public struct Bar { public var x:Foo }
    var t = defaultValue();
    t = someFactoryThatReturnsBar(); // here's our problem
    

    At the problem line, Swift thinks that t has been initialized because that's what Swift's semantics say: you cannot have a variable of a value type that is uninitialized. Except that it is because default breaks those semantics. When you do the assignment, Swift emits a call into the value witness table to destroy the existing type. This will include code that will call release on the field x, because Swift semantics say that instances of objects are never nil. And then you get a runtime crash.

    However, I had cause to interoperate with Swift from another language and I had to pass in an optional type. Unfortunately, Swift doesn't provide me with a way to construct an optional at runtime because of reasons (at least I haven't found a way), and I can't easily mock one because optionals are implemented in terms of a generic enum and enums use a poorly documented 5 strategy implementation to pack the payload of an enum.

    I worked around this by passing a tuple that I'm going to call a Medusa tuple just for grins: (value: T, present: Bool) which has the contract that if present is true, then value is guaranteed to be valid, invalid otherwise. I can use this safely now to interop:

    public func toOptional(optTuple: (value:T, present:Bool)) -> T? 
    {
        if optTuple.present { return optTuple.value }
        else { return nil }
    }
    
    public func fromOptional(opt: T?) -> (T, Bool)
    {
        if opt != nil { return (opt!, true) }
        else {
            return (defaultValue(), false)
        }
    }
    

    In this way, my calling code passes in a tuple instead of an optional and the receiving code and turn it into an optional (and the reverse).

提交回复
热议问题