In the example below, why can Swift not assign an explicitly type property of String or Double to a generic property of type T? However, if I use a generic constraint to specify an explicit type, then it does work?

import Foundation

class BadBox<T> {
  var value: T
  
  init(_ value: String) {
    self.value = value // Cannot assign value of type 'String' to type 'T'.
  }
  
  init(_ value: Double) {
    self.value = value // Cannot assign value of type 'String' to type 'T'.
  }
}

class GoodBox<T> {
  var value: T

  init(_ value: T) where T == String {
    self.value = value // This is OK and compiles fine.
  }

  init(_ value: T) where T == Double {
    self.value = value // As does this.
  }
}

It occasionally comes up that I want to define a class who's public interface explicitly defines the types of properties it can be used with, but who's internal implementation has generic components.

Creating an empty Protocol just to use as a generic constraint for such a class, ex: Boxable, seems needlessly verbose to me, though perhaps that's the Swift way?

In both BadBox<T> and GoodBox<T>, T is a generic parameter. This however doesn't mean that T can be any type. T is fixed for each instance.

The error message in the first initializer is self-explanatory: you cannot assign a value of type String to a value of type T. init(_ value: String), as defined, would be available independently of the generic parameter T and this means that the following would be allowed:

let string = "Swift"

let x = BadBox<String>(string)  // uses BadBox<String>.init(_ value: String)
let y = BadBox<Double>(string)  // uses BadBox<Double>.init(_ value: String)

In the first example T == String, so x.value is a String and self.value = value would be an allowed assignment.
In the second example, however, T == Double, so y.value is a Double, but you cannot assign a String value to a Double instance.

1 Like