You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中如何为实际可相等的非Equatable协议类型实现查找方法?

简化实现Container的indexOfItem方法方案

问题背景

现有两个Swift协议定义如下:

protocol Container {
    var items: [Item] {get}
}

protocol Item {
    var name: String {get}
}

需要为Container扩展indexOfItem方法来查找指定Item的索引,但直接调用firstIndex(of:)会因为Item协议无法遵循Equatable而报错。已知所有实际的Item实现类都遵循Equatable,以下是两种无需类型擦除的简化实现方案。


方案一:修改Container协议,添加关联类型约束

通过给Container协议定义关联类型,并约束该类型同时遵循Item和Equatable,从根源上满足firstIndex(of:)的类型要求。

protocol Container {
    associatedtype ItemType: Item & Equatable
    var items: [ItemType] { get }
}

protocol Item {
    var name: String { get }
}

// 扩展实现indexOfItem
extension Container {
    func indexOfItem(_ item: ItemType) -> Int? {
        items.firstIndex(of: item)
    }
}

// 示例实现类
struct Book: Item, Equatable {
    var name: String
}

struct Shelf: Container {
    typealias ItemType = Book
    var items: [Book]
}

// 使用示例
let shelf = Shelf(items: [Book(name: "Swift实战"), Book(name: "iOS进阶")])
let targetBook = Book(name: "iOS进阶")
print(shelf.indexOfItem(targetBook)) // 输出 Optional(1)

优势:类型安全,编译器能在编译阶段严格校验类型,无需运行时类型转换。


方案二:不修改原协议,通过扩展约束或泛型实现

如果无法修改原Container协议,可以通过扩展的where子句或泛型参数,在运行时将Item转换为具体的遵循Equatable的类型进行比较。

写法1:泛型参数约束

protocol Container {
    var items: [Item] { get }
}

protocol Item {
    var name: String { get }
}

extension Container {
    func indexOfItem<T: Item & Equatable>(_ item: T) -> Int? {
        items.firstIndex { ($0 as? T) == item }
    }
}

// 示例使用
struct Book: Item, Equatable {
    var name: String
}

struct Shelf: Container {
    var items: [Item] = [Book(name: "Swift实战"), Book(name: "iOS进阶")]
}

let shelf = Shelf()
let targetBook = Book(name: "iOS进阶")
print(shelf.indexOfItem(targetBook)) // 输出 Optional(1)

写法2:扩展where子句约束数组元素类型

protocol Container {
    var items: [Item] { get }
}

protocol Item {
    var name: String { get }
}

extension Container where [Item].Element: Equatable {
    func indexOfItem(_ item: Item) -> Int? {
        guard let equatableItem = item as? [Item].Element else {
            return nil
        }
        return items.firstIndex { ($0 as? [Item].Element) == equatableItem }
    }
}

// 示例使用同写法1

优势:无需修改原有协议,适配已有代码结构;注意:依赖运行时类型转换,若传入的item类型与数组元素类型不匹配,会返回nil。


内容的提问来源于stack exchange,提问作者Vladimir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 14:55:19