Swift 通用函数在测试中不编译
Swift generic function does not compile in testing
我在 Swift 中编译测试用例时遇到问题。看起来编译器正在丢失有关模板类型的信息,但其他通用方法工作正常。我错过了什么?
public class MatchNorm {
public static func resolve1<T:SequenceType where T.Generator.Element:MatchNormElement>(list: T, lti: LinearTransformation, accuracy: Double) -> LinearTransformation {
// no problem
return MatchNorm.resolve1(list, lti: lti, accuracy: accuracy)
}
public static func resolve2<T:SequenceType where T.Generator.Element:MatchNormElement>(list: T, lti: LinearTransformation, accuracy: Double) -> LinearTransformation {
for elem in list {
print(elem.x)
}
return lti
}
}
public class MatchNormTest: XCTestCase {
func testMatchNorm1() {
var list = [MatchNormElement]()
// compilation error here!
let ll = MatchNorm.resolve1(list, lti: LinearTransformation(1), accuracy: 0.001)
// MatchNormTest.swift:70:29: Cannot invoke 'resolve1' with an argument list of type '([MatchNormElement], lti: LinearTransformation, accuracy: Double)'
// MatchNormTest.swift:70:29: Expected an argument list of type '(T, lti: LinearTransformation, accuracy: Double)'
}
}
更新
MatchNormElement 是一个协议,所以我把它改成了具体类型。现在可以了。
func testMatchNorm1() {
var list = [Measurment]()
// works fine
let ll = MatchNorm.resolve1(list, lti: LinearTransformation(1), accuracy: 0.001)
}
这对编译器来说是合理的。 Swift 允许将具体类型转换为基于协议的变量赋值。
但是,Swift 不允许通过相互转换将一组具体类型转换为 set/array 协议。这是合理的,原因如下:
假设我们有这样的构造:
protocol IntType {}
extension Int: IntType{}
现在让我们做一件显而易见的事情:
let item = 12
let item2:IntType = item
然后是一个看起来很明显的:这有充分的理由无法编译。
let a = [1,2,3]
let b: [IntType] = a
让我们在继续之前检查每种类型的大小:
sizeofValue(item) //8
sizeofValue(item2) //40
数组是 8 字节的传染性内存。 Array 也是一个 40 字节的传染性内存。
我们这样做的时候:
let b: [IntType] = a
我们基本上告诉编译器将 8 字节的数组转换为 40 字节的数组并存储它。现在由于数组具有传染性,它必须销毁或重新洗牌,这是昂贵的这样做的任务。这阻碍了性能,显然你失去了类型安全。
编译器可以进行这种转换,但是 Swift 团队有充分的理由决定,如果用户想要进行这种类型转换,则需要明确说明,原因有两个:性能和 类型安全。
我在 Swift 中编译测试用例时遇到问题。看起来编译器正在丢失有关模板类型的信息,但其他通用方法工作正常。我错过了什么?
public class MatchNorm {
public static func resolve1<T:SequenceType where T.Generator.Element:MatchNormElement>(list: T, lti: LinearTransformation, accuracy: Double) -> LinearTransformation {
// no problem
return MatchNorm.resolve1(list, lti: lti, accuracy: accuracy)
}
public static func resolve2<T:SequenceType where T.Generator.Element:MatchNormElement>(list: T, lti: LinearTransformation, accuracy: Double) -> LinearTransformation {
for elem in list {
print(elem.x)
}
return lti
}
}
public class MatchNormTest: XCTestCase {
func testMatchNorm1() {
var list = [MatchNormElement]()
// compilation error here!
let ll = MatchNorm.resolve1(list, lti: LinearTransformation(1), accuracy: 0.001)
// MatchNormTest.swift:70:29: Cannot invoke 'resolve1' with an argument list of type '([MatchNormElement], lti: LinearTransformation, accuracy: Double)'
// MatchNormTest.swift:70:29: Expected an argument list of type '(T, lti: LinearTransformation, accuracy: Double)'
}
}
更新
MatchNormElement 是一个协议,所以我把它改成了具体类型。现在可以了。
func testMatchNorm1() {
var list = [Measurment]()
// works fine
let ll = MatchNorm.resolve1(list, lti: LinearTransformation(1), accuracy: 0.001)
}
这对编译器来说是合理的。 Swift 允许将具体类型转换为基于协议的变量赋值。
但是,Swift 不允许通过相互转换将一组具体类型转换为 set/array 协议。这是合理的,原因如下:
假设我们有这样的构造:
protocol IntType {}
extension Int: IntType{}
现在让我们做一件显而易见的事情:
let item = 12
let item2:IntType = item
然后是一个看起来很明显的:这有充分的理由无法编译。
let a = [1,2,3]
let b: [IntType] = a
让我们在继续之前检查每种类型的大小:
sizeofValue(item) //8
sizeofValue(item2) //40
数组是 8 字节的传染性内存。 Array 也是一个 40 字节的传染性内存。
我们这样做的时候:
let b: [IntType] = a
我们基本上告诉编译器将 8 字节的数组转换为 40 字节的数组并存储它。现在由于数组具有传染性,它必须销毁或重新洗牌,这是昂贵的这样做的任务。这阻碍了性能,显然你失去了类型安全。
编译器可以进行这种转换,但是 Swift 团队有充分的理由决定,如果用户想要进行这种类型转换,则需要明确说明,原因有两个:性能和 类型安全。