Swift中泛型DArray协议下Vector乘法类型不匹配问题求助
首先咱们得搞清楚为啥会报错:虽然Element和T.Element都遵循Numeric协议,但Swift里Numeric定义的乘法运算符*要求两个操作数必须是完全相同的类型——比如Int * Int、Double * Double,不同的数值类型(比如Int和Double)之间并没有直接的*运算符支持,编译器不知道该怎么把这两个不同类型的数值相乘,所以才会抛出类型转换的错误。
另外你原来的代码里还有个小问题:result变量只声明了但没初始化,这也是后续需要修正的点。
接下来给你两种常见的解决思路,你可以根据自己的需求选择:
思路1:要求两个操作数的元素类型必须一致
如果你的场景只需要处理元素类型相同的DArray(比如Vector<Int>和另一个DArray类型但元素也是Int),那可以给泛型T添加类型约束,让T.Element和当前Vector的Element完全匹配:
protocol DArray: Sequence where Element: Numeric { var elements: [Element] { get set } subscript(index: Int) -> Element { get set } static func *(lhs: Self, rhs: Self) -> Self } struct Vector<Element: Numeric>: DArray { var elements: [Element] init(_ elements: [Element] = []) { self.elements = elements } // 实现下标 subscript(index: Int) -> Element { get { elements[index] } set { elements[index] = newValue } } // 实现同类型的乘法(满足DArray协议要求) static func *(lhs: Vector, rhs: Vector) -> Vector { let minCount = min(lhs.elements.count, rhs.elements.count) var resultElements = [Element]() for i in 0..<minCount { resultElements.append(lhs[i] * rhs[i]) } return Vector(resultElements) } // 扩展支持和其他DArray类型相乘,但要求元素类型一致 static func *<T: DArray>(lhs: Self, rhs: T) -> Self where T.Element == Element { let minCount = min(lhs.elements.count, rhs.elements.count) var resultElements = [Element]() for i in 0..<minCount { // 现在类型完全匹配,可以直接相乘 resultElements.append(lhs[i] * rhs[i]) } // 这里可以根据需求处理长度不一致的情况:比如补0、截断或者报错 return Vector(resultElements) } }
思路2:支持不同数值类型的元素相乘(需要类型转换)
如果需要支持不同数值类型的元素相乘(比如Vector<Double>和DArray类型的Int元素),那你需要处理类型转换——因为不同Numeric类型之间的转换需要显式处理(比如Int转Double很安全,但Double转Int会丢失精度)。
你可以利用Numeric协议的初始化方法,把其中一个类型转换成另一个类型,比如把T.Element转换成当前Vector的Element:
static func *<T: DArray>(lhs: Self, rhs: T) -> Self where T.Element: Numeric { let minCount = min(lhs.elements.count, rhs.elements.count) var resultElements = [Element]() for i in 0..<minCount { // 尝试把rhs的元素转换成Element类型 guard let convertedRhsElement = Element(exactly: rhs[i]) else { // 转换失败的处理逻辑:比如抛出错误、返回默认值等 fatalError("Cannot convert \(rhs[i]) to \(Element.self)") } resultElements.append(lhs[i] * convertedRhsElement) } return Vector(resultElements) }
这里用了Element(exactly:)初始化器,它会尝试安全转换,如果转换失败(比如把Double(1.5)转成Int)会返回nil,你可以根据自己的业务需求处理这种失败情况——比如抛出自定义错误、用Element(rhs[i])强制转换(但可能会丢失精度),或者跳过该元素等。
补充说明
- 不管用哪种思路,都要注意处理两个数组长度不一致的情况:比如只计算到较短数组的长度,或者给较短的数组补0后再计算,具体逻辑取决于你的业务需求。
- 如果你的
DArray协议需要更灵活的乘法支持,也可以考虑修改协议,让乘法的结果类型是泛型的,而不是固定返回Self,这样可以返回元素类型为两者乘积类型的新数组。
内容的提问来源于stack exchange,提问作者Ellie

