abreis abreis - 3 months ago 11
Swift Question

Generics: is there a protocol for InitializableWithString?

I'm trying to build a generic struct that can be initialized from a String.

Minimal example:

struct Example<T> {
var data: T

init(fromString string: String) {
data = T(string)
}
}


This fails, naturally, because we must ensure T can be init() from a String.

I could not find a protocol for this, so I tried to create it:

protocol InitializableWithString { init?(_: String) }

struct Example<T: InitializableWithString> { ... }


but experimenting with an Int gives:

let testVar = Example<Int>(fromString: "12")



error: type 'Int' does not conform to protocol 'InitializableWithString'


But Ints do have a failable initializer that takes a String argument.

> let intFromString = Int("12")
intFromString: Int? = 12


Is there a way to accomplish what I'm trying to do here?

Answer

AFAIK a protocol representing a Type that can be created with a String does not exist.

You defined your own which is fine but there are a few errors.

1. Naming the parameter

protocol InitializableWithString {
    init?(string:String)
}

2. Making Int conform to InitializableWithString

The Int struct has an initializer used to create an Int from a String, it is used when we write

Int("123")

This initializer has the following signature

public init?(_ text: String, radix: Int = default)

The missing part of this solution is extending Int to make it conform to InitializableWithString like shown below

extension Int: InitializableWithString {
    init?(string: String) {
        self.init(string)
    }
}

3. Making the init of Example failable as well

Since the initializer of InitializableWithString if failable, you could not be able to build a T type. In this case what should the Exampleinit` do? The easiest solution is to make it fail so

struct Example<T where T: InitializableWithString> {
    var data: T

    init?(string: String) {
        guard let data = T(string: string) else { return nil }
        self.data = data
    }
}

Conclusion

Now you can write

let example = Example<Int>(string: "123")
if let example = example {
    print(example.data)
}

More

Now you can make more types conform to InitializableWithString and then using them to build new Example(s) values.

Comments