Нет ли по умолчанию (T) в Swift?

Я пытаюсь перенести пример Matrix из книги Swift в общий.

Вот что я получил до сих пор:

struct Matrix<T> {
    let rows: Int, columns: Int
    var grid: T[]

    init(rows: Int, columns: Int, repeatedValue: T) {
        self.rows = rows
        self.columns = columns

        grid = Array(count: rows * columns, repeatedValue: repeatedValue)
    }

    func indexIsValidForRow(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }

    subscript(row: Int, column: Int) -> T {
        get {
            assert(indexIsValidForRow(row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValidForRow(row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}

Обратите внимание, что мне пришлось передать repeatedValue: T в конструктор.

В С# я бы просто использовал default(T), который был бы 0 для чисел, false для booleans и null для ссылочных типов. Я понимаю, что Swift не разрешает nil для необязательных типов, но мне все же интересно, если передача явного параметра является единственным способом, или если у меня есть какой-то эквивалент default(T) там.

Ответы

Ответ 1

Нет. Swift заставляет вас указывать значение по умолчанию, точно так же, как вы обрабатываете переменные и поля. Единственный случай, когда Swift имеет концепцию значения по умолчанию для необязательных типов, где nil (Optional.None).

Ответ 2

Если "ДА". Вы можете использовать ограничения протокола, чтобы указать требование, чтобы ваш общий класс или функция работали только с типами, реализующими функцию init по умолчанию (без параметров). Последствия этого, скорее всего, будут плохими (это не работает так, как вы думаете, что оно делает), но это самое близкое к тому, что вы просили, вероятно, ближе, чем ответ "НЕТ".

Для меня я нашел это лично полезным при разработке нового родового класса, а затем, в конце концов, я удалю ограничение и исправлю оставшиеся проблемы. Требование только типов, которые могут принимать значение по умолчанию, ограничит полезность вашего общего типа данных.

public protocol Defaultable
{
  init()
}

struct Matrix<Type: Defaultable>
{
  let rows: Int
  let columns: Int
  var grid: [Type]

  init(rows: Int, columns: Int)
  {
    self.rows = rows
    self.columns = columns

    grid = Array(count: rows * columns, repeatedValue: Type() )
  }
}

Ответ 3

Существует способ получить эквивалент default(T) в swift, но он не является бесплатным и имеет связанную с ним опасность:

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

Теперь это явно хак, потому что мы не знаем, инициализируется ли alloc() чему-то познаваемому. Это все 0? Вещи, оставшиеся в куче? Кто знает? Более того, сегодня это может быть завтра.

Фактически, использование возвращаемого значения для ничего, кроме заполнителя, опасно. Скажем, у вас есть такой код:

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

В строке проблем Swift считает, что t был инициализирован, потому что семантика Swift говорит: вы не можете иметь переменную типа значения, которая не инициализирована. За исключением того, что это потому, что default<T> нарушает эти семантики. Когда вы выполняете задание, Swift выдает вызов в таблицу показаний значения, чтобы уничтожить существующий тип. Это будет включать код, который вызовет release в поле x, потому что семантика Swift говорит, что экземпляры объектов никогда не nil. И тогда вы получите краху времени выполнения.

Однако у меня была причина взаимодействовать с Swift с другого языка, и мне пришлось пройти дополнительный тип. К сожалению, Swift не дает мне способ построить необязательный во время выполнения из-за причин (по крайней мере, я не нашел способа), и я не могу легко их издеваться, потому что опции реализуются с точки зрения общего перечисления и перечисления используют слабо документированную стратегию 5, чтобы упаковать полезную нагрузку перечисления.

Я работал над этим, передавая кортеж, который я собираюсь называть кортежем Medusa только для усмешек: (value: T, present: Bool), у которого есть контракт, что если present есть true, то value гарантированно будет действителен, недействителен. Теперь я могу использовать это безопасно для взаимодействия:

public func toOptional<T>(optTuple: (value:T, present:Bool)) -> T? 
{
    if optTuple.present { return optTuple.value }
    else { return nil }
}

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

Таким образом, мой код вызова переходит в кортеж вместо необязательного и принимающего кода и превращает его в необязательный (и наоборот).