泛型:是否有InitializableWithString的协议?

时间:2016-08-12 21:13:38

标签: swift generics

我正在尝试构建一个可以从String初始化的通用结构。

最小例子:

struct Example<T> {
    var data: T

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

这自然会失败,因为我们必须确保T可以是来自String的init()。

我找不到这个协议,所以我试图创建它:

protocol InitializableWithString { init?(_: String) }

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

但是尝试使用Int:

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

错误:类型'Int'不符合协议'InitializableWithString'

但Ints确实有一个可用的初始化器,它接受一个String参数。

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

有没有办法完成我在这里要做的事情?

2 个答案:

答案 0 :(得分:1)

AFAIK表示可以使用String创建的类型的协议不存在。

你定义了自己的,但是有一些错误。

1。命名参数

protocol InitializableWithString {
    init?(string:String)
}

2。使Int符合InitializableWithString

Int结构体有一个初始化程序,用于从Int创建String,在我们编写时使用它

Int("123")

此初始化程序具有以下签名

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

此解决方案的缺失部分是Int,以使其符合InitializableWithString,如下所示

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

3。使init的{​​{1}}同样可以

由于Example的初始值设定项可用,因此您无法构建InitializableWithString类型。在这种情况下,T init`应该做什么?最简单的解决方案是让它失败,所以

Example

结论

现在你可以写

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

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

更多

现在,您可以使更多类型符合let example = Example<Int>(string: "123") if let example = example { print(example.data) } ,然后使用它们来构建新的InitializableWithString值。

答案 1 :(得分:1)

问题是你没有明确地将Int与你的InitializableWithString协议相符,因此即使它可能含蓄地符合,Swift的类型系统也不会认识到这一点。

您可以通过扩展程序明确地遵守它:

extension Int : InitializableWithString {}

但是,我们仍然会收到错误:

  

Type'Int'不符合协议'InitializableWithString'

问题现在Int可以使用String进行初始化,而through this initialiser则是enter image description here,它有两个参数 - {{ 1}}参数恰好有一个默认值:

radix:

因此,Swift无法识别/// Construct from an ASCII representation in the given `radix`. /// /// If `text` does not match the regular expression /// "[+-]?[0-9a-zA-Z]+", or the value it denotes in the given `radix` /// is not representable, the result is `nil`. public init?(_ text: String, radix: Int = default) 可以满足Int要求。因此,解决方案是实现满足此要求的初始化程序,并使用默认的init?(_: String)值将调用转发到init?(_:radix:)初始化程序:

radix

或者,如果您还希望能够处理非十进制数字表示,则将协议更改为需要extension Int : InitializableWithString { init?(_ str: String) { self.init(_:radix:)(str) // the (_:radix:) is used in order to disambiguate } } 参数:

radix:

有了这个,你不需要实现额外的初始化,因为protocol InitializableWithString { init?(_: String, radix:Int) } 直接满足了这个要求。