Reputation: 975
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
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