Search code examples
swiftgenericstypessubscript

Swift subscript with generic data type


I am trying to program a two-dimensional data storage struct for various data types. However, I am struggling with the subscript for setting the data due 'Cannot assign value of type 'T' to subscript of type 'T' errors. Any help is much appreciated!

struct dataMatrix<T> : Sequence, IteratorProtocol {
    var rows: Int, columns: Int
    var data: [T]
    var position = 0
    
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        data = Array<T>()
    }
    
    func valueAt(column: Int, row: Int) -> T? {
        guard column >= 0 && row >= 0 && column < columns else {
                return nil
            }

        let indexcolumn = column + row * columns

            guard indexcolumn < data.count else {
                return nil
            }

            return data[indexcolumn]
        }
    }
    
    subscript<T>(column: Int, row:Int) -> T?{
        get{
            return valueAt(column: column, row: row) as? T
        }
        set{
            data[(column * row) + column] = (newValue as! T) // does not compile
        }   
    }
    
    // sequence iterator protorocl methods
    mutating func next() -> String? {
        if position <= data.count{
            print(position)
            defer { position += 1 }
            return "\(position)"
        }else{
            defer {position = 0}
            return nil
        }
    }
}

Solution

  • subscript<T>(column: Int, row:Int) -> T?{
    

    defines a generic method with a type placeholder T which is unrelated to the generic type T of struct dataMatrix<T>. The solution is simple: Remove the type placeholder:

    subscript(column: Int, row: Int) -> T? {
        // ...
    }
    

    That makes also the type casts inside the getter and setter unnecessary. You only have to decide what to do if the setter is called with a nil argument (e.g.: nothing):

    subscript(column: Int, row: Int) -> T? {
        get {
            return valueAt(column: column, row: row)
        }
        set {
            if let value = newValue {
                data[(column * row) + column] = value
            }
        }
    }
    

    Another option is to make the return type of the subscript method non-optional, and treat invalid indices as a fatal error (which is how the Swift Array handles it):

    subscript(column: Int, row: Int) -> T {
        get {
            guard let value = valueAt(column: column, row: row) else {
                fatalError("index out of bounds")
            }
            return value
        }
        set {
            data[(column * row) + column] = newValue
        }
    }