Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use generic default parameters

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

like image 964
J. Doe Avatar asked Dec 07 '25 18:12

J. Doe


1 Answers

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()
like image 101
Sulthan Avatar answered Dec 09 '25 16:12

Sulthan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!