Maiaux
Maiaux

Reputation: 975

subscripts and protocols in swift

I am defining a Protocol, for abstraction purposes, having a "subscript" function with both a getter and a setter. Then I am defining a class implementing that protocol.

Short version of the problem: if I use the subscript on an object of the class (as an lvalue, and thus using the setter), everything works as expected. If I do it on an object just declared of the protocol type, I get a "Cannot assign to the result of this expression" error.

Long version. I have a board of Int. A board is a 2D matrix. I expose the Board type via the BoardType protocol.

protocol BoardType {
    var width: Int { get }
    var height: Int { get }
    subscript(x: Int, y: Int) -> Int { get set }
}

class Board: BoardType {

    let width, height: Int
    var matrix: Array2D<Int>

    init(width: Int, height: Int) {
        self.width = width
        self.height = height
        matrix = Array2D<Int>(cols: width, rows: height, defaultValue: 0)
    }

    subscript(x: Int, y: Int) -> Int {
        get {
            return matrix[x, y]
        }
        set {
            matrix[x, y] = newValue
        }
    }
}

The implementation of Array2D is standard:

class Array2D<T> {

    var cols: Int, rows: Int
    var matrix: [T]

    init(cols: Int, rows:Int, defaultValue: T) {
        self.cols = cols
        self.rows = rows
        matrix = Array(count: cols*rows, repeatedValue: defaultValue)
    }

    subscript(x: Int, y: Int) -> T {
        get {
            return matrix[cols * y + x]
        }
        set {
            matrix[cols * y + x] = newValue
        }
    }

    func colCount() -> Int {
        return self.cols
    }

    func rowCount() -> Int {
        return self.rows
    }
}

Now, if I do the following, it works:

    let board = Board(width: 4, height: 4)
    board[1, 1] = 10

Instead, if I use the prototype, I get the error

    let board : BoardType = Board(width: 4, height: 4)
    board[1, 1] = 10

Why?

Upvotes: 3

Views: 6301

Answers (1)

Nate Cook
Nate Cook

Reputation: 93286

The compiler is doing that because you've declared the second one with let and it could be a value type, in which case the subscript setter would be a mutating expression. Two fixes - either use var when declaring with BoardType:

var board : BoardType = Board(width: 4, height: 4)
board[1,1] = 10

or make BoardType a class protocol:

protocol BoardType: class {
    var width: Int {get}
    var height: Int {get}
    subscript(x:Int, y:Int) -> Int {get set}
}

Upvotes: 20

Related Questions