代码之家  ›  专栏  ›  技术社区  ›  Reinhard Männer

无法将协议的一般关联类型的值转换为预期的参数类型

  •  1
  • Reinhard Männer  · 技术社区  · 7 年前

    为了学习Swift泛型,我编写了一个创建TableView数据源的函数,即元素的二维(节、行)数组。 元素类型应为泛型,创建的数据源应使用元素的唯一值初始化。

    我声明了可能的元素类型采用的协议:

    protocol UniqueInit {
        associatedtype T
        static func uniqueInit() -> T
    }
    

    以及 dataSource 功能。
    在这里, nrRowsInSection 是一个可变参数:给定参数的数目定义节的数目,参数的值定义各自节中的行数。

    static func dataSource<T: UniqueInit>(nrRowsInSection: Int...) -> [[T]] {
        var result: [[T]] = []
        for nrRows in nrRowsInSection {
            var row: [T] = []
            for _ in 0 ..< nrRows {
                row.append(T.uniqueInit())
            }
            result.append(row)
        }
        return result
    }  
    

    此函数不编译。声明

    row.append(T.uniqueInit())  
    

    给出错误:

    Argument type 'T.T' does not conform to expected type 'UniqueInit'  
    Cannot convert value of type 'T.T' (associated type of protocol 'UniqueInit') to expected argument type 'T' (generic parameter of static method 'dataSource(nrRowsInSection:)')  
    

    很明显, static func uniqueInit() 被认为是错误的,但为什么呢?
    正确的实现是什么?

    2 回复  |  直到 7 年前
        1
  •  2
  •   dan    7 年前

    一般的 T 在您的函数和关联类型中 T 在你的协议里不一样 T . 在函数内部, T 正在引用正在实现协议的类型,因此关联类型为 T.T 在函数内部。数组和返回值必须使用 T.T .

    这还意味着您需要为函数添加一个额外的参数,因为 [[T.T]] 返回值不足以让编译器推断什么类型 T 是。

    这应该有效(我将通用参数更改为 U 因为所有的 T S令人困惑):

    func dataSource<U: UniqueInit>(initializer: U.Type, nrRowsInSection: Int...) -> [[U.T]] {
        var result: [[U.T]] = []
        for nrRows in nrRowsInSection {
            var row: [U.T] = []
            for _ in 0 ..< nrRows {
                row.append(U.uniqueInit())
            }
            result.append(row)
        }
        return result
    }
    

    或者,可以将函数定义为 UniqueInit 这将消除对仿制药的需求:

    extension UniqueInit {
        func dataSource(nrRowsInSection: Int...) -> [[T]] {
            var result: [[T]] = []
            for nrRows in nrRowsInSection {
                var row: [T] = []
                for _ in 0 ..< nrRows {
                    row.append(Self.uniqueInit())
                }
                result.append(row)
            }
            return result
        }
    }
    
        2
  •  0
  •   user4527951    7 年前

    看看下面的实现是否适合您。

    protocol UniqueInit {
        static func uniqueInit() -> Self
    }
    
    func dataSource<T: UniqueInit>(nrRowsInSection: Int...) -> [[T]] {
        var result: [[T]] = []
        for nrRows in nrRowsInSection {
            var row: [T] = []
            for _ in 0 ..< nrRows {
                row.append(T.uniqueInit())
            }
            result.append(row)
        }
        return result
    }
    

    我认为上面数据源实现中的T应该替换为uniqueinit。