3

This is my code:

class Person {
    init<T: RawRepresentable>(raw: T = Child.johnDoe) {}
}

enum Child: String {
    case johnDoe
}

It doesn't compile. The error is:

Default argument value of type 'Child' cannot be converted to type 'T'

Why can't it be converted? According to the docs, Child.someEnum is RawRepresentable:

Enumerations with Raw Values For any enumeration with a string, integer, or floating-point raw type, the Swift compiler automatically adds RawRepresentable conformance. When defining your own custom enumeration, you give it a raw type by specifying the raw type as the first item in the enumeration’s type inheritance list.

This also compiles:

class Person {
    static func accept<T: RawRepresentable>(raw: T) where T.RawValue == String {}
}

enum Child: String {
    case johnDoe
}

Person.accept(raw: Child.johnDoe)

Why doesn't it work as a default parameter?

Use case: I want to accept any RawPresentable value, so I can extract the rawValue from it. I want to provide a default value (always "") (I just create a struct with rawValue = ""). I do not want to create multiple initializers, since I got some subclasses and that would get a mess. The best for me is just to provide a default RawRepresentable object.

When I add a cast: init(ty: T = (Child.johnDoe as! T)) where T.RawValue == String {

}

Or make it nillable:

(ty: T? = nil)

It compiles. But now I can not call:

let x = Person()

It gives the error:

Generic parameter 'T' could not be inferred

Marcy
  • 4,611
  • 2
  • 34
  • 52
J. Doe
  • 12,159
  • 9
  • 60
  • 114
  • 2
    Possible duplicate of [Default parameter as generic type](https://stackoverflow.com/questions/38326992/default-parameter-as-generic-type) – Dmitriy Kirakosyan Feb 12 '19 at 19:35
  • @DmitriyKirakosyan This question and that question have the same title, but seeing the answers and actual question, I think the content of my question is different :). The answers stated in that question doesn't work for my case. – J. Doe Feb 12 '19 at 19:37
  • What is your use case that the answers in that question don't work for? – dan Feb 12 '19 at 19:40
  • @dan I added the use case. The questions in that answer states I need to add a initializer, but when I do that it still won't work. – J. Doe Feb 12 '19 at 19:43
  • To make it work as you want, you have to add a specific `init`, eg: `init(raw: Child = Child.johnDoe)` – Dmitriy Kirakosyan Feb 12 '19 at 19:59

1 Answers1

3

This is certainly possible. However, you have to use your own protocol and add the default value to that protocol:

protocol MyRawRepresentable: RawRepresentable {
    static var defaultValue: Self { get }
}

class Person {
    init<T: MyRawRepresentable>(raw: T = T.defaultValue) {}
}

enum Child: String, MyRawRepresentable {
    case johnDoe

    static let defaultValue: Child = .johnDoe
}

There is another issue though. How will you specify the generic type if you use the default parameter value and all you will have will be just Person.init()?

The only solution I see is to also specify a default generic type which means you actually want:

class Person {
   init<T: RawRepresentable>(raw: T) {
   }

   convenience init() {
       self.init(raw: Child.johnDoe)
   }
}

Unless you actually want to make Person itself a generic class because then you could just use

Person<Child>.init()
Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • Yes I ran into the problem trying yo contruct a Person without arguments, i editted my question about the compile error I get. I dont really care about the generic type, but Swift wants me somehow to specify a type... – J. Doe Feb 12 '19 at 19:56