【问题标题】:Ambiguous type inference模棱两可的类型推断
【发布时间】:2016-11-05 22:06:25
【问题描述】:

我从 Swift 3.0.1 收到一个编译器错误,这让我很困惑。该错误表明计算属性的类型存在歧义,但我不知道如何。

我有一个协议Generic 和一个属性root。该协议有一个通用约束,即root 必须是Root 类型的子类。

class Root { }

protocol Generic {
    associatedtype RootType: Root
    var root: RootType { get }
}

然后我定义了一个协议扩展,它声明:

如果GenericRoot 的子类,则从root 属性返回self

所以基本上:如果它已经是Root,你可以转发self

extension Generic where Self: Root {
    var root: Self {
        return self
    }
}

我还有一个GenericWrapper 类,它是Root 的子类,并包装了Generic 的一个实例(使用Generic 代理执行Root 操作)。

class GenericWrapper<T: Generic>: Root {
    var generic: T

    init(generic: T) {
        self.generic = generic
    }
}

最后,我定义了一个Specialised 协议,并对其进行了扩展,声明如下:

如果Specialised 实现Generic,则从root 属性返回GenericWrapper

protocol Specialised { }
extension Specialised where Self: Generic {
    var root: GenericWrapper<Self> {
        get {
            return GenericWrapper(generic: self)
        }
    }
}

然后,当我尝试实现一个实现 GenericSpecialised 的类时,我收到了这个错误。

class SpecialisedImplementation: Generic, Specialised {
    // errors:
    //   Ambiguous inference of associated type 'RootType': 'GenericWrapper<SpecialisedImplementation>' vs. 'SpecialisedImplementation'
    //   Matching requirement 'root' to this declaration inferred associated type to 'GenericWrapper<SpecialisedImplementation>'
    //   Matching requirement 'root' to this declaration inferred associated type to 'SpecialisedImplementation'
}

我感到困惑的原因是,当Generic: Root 时,SpecialisedImplementation 类与扩展名到Generic 的要求相匹配,但SpecialisedImplementation 不继承自Root 所以它应该不一定?

【问题讨论】:

标签: swift generics swift3


【解决方案1】:

诊断令人困惑;这里真正的问题是它太循环了,超出了编译器可以处理的范围。

它尝试解析Generic,并发现它不能不假设SpecialisedImplementationRoot 的子类(这不是真的,但它迫切希望找到一种让它工作的方法)。它试图让Specialised 工作,但它只能在Generic 已经工作的情况下这样做,但让Generic 工作的唯一方法是让Root 工作。

您希望它同时假设您所说的一切都是真实的,但它并不那么聪明。它试图零碎地构建它,一次一个协议,并且感到困惑。在 bugs.swift.org 上打开错误报告。

(但这也几乎可以肯定疯狂地太复杂了。特别是,如果可能的话,我会努力摆脱 Root 类;混合协议、类和泛型在一起是许多非常令人困惑的编译器问题的秘诀。)

【讨论】:

  • 谢谢罗伯。我同意这是非常复杂的(不幸的是,Root 映射到我抽象中的 Foundation 类,所以我无法摆脱它)。我的假设是编译器应该忽略第一个扩展,因为我只实现Generic 并在该类/结构中提供var root 的实现是完全有效的。但是一旦我提供了SpecialisedImplementation 扩展,它提供了一个推断的RootType,它就会变得混乱并给我一个错误。尽管很复杂,但我认为我正在尝试做的是技术上有效,所以我可能会打开一个错误。
【解决方案2】:

我认为这是一个编译器错误并打开了bug report。对于遇到类似问题的其他人,我已经设法通过从 Generic 扩展中删除 Self 要求来解决它:

class Root { }

protocol Generic {
    associatedtype RootType: Root
    var root: RootType { get }
}

extension Generic where Self: Root {
    // ===================
    // don't use Self here
    // ===================
    var root: Root {
        return self
    }
}

class GenericWrapper<T: Generic>: Root {
    var generic: T

    init(generic: T) {
        self.generic = generic
    }
}

protocol Specialised { }
extension Specialised where Self: Generic {
    var root: GenericWrapper<Self> {
        get {
            return GenericWrapper(generic: self)
        }
    }
}

class SpecialisedImplementation: Generic, Specialised {
    // no errors!
}

【讨论】:

  • 我从编译器团队得知我遇到的错误已经在 Swift 主分支上修复,因此应该在未来的版本中修复。
【解决方案3】:

我正在解决一般性错误。

匹配要求 'afuncName(:)' 到此声明推断 与“SomeType”关联的类型

这意味着:

  • 协议有一个关联类型。
  • associatedType 用于多个函数,但传递的是 不同 类型。因此不满足协议要求。

想象一下我写了这个协议。

protocol Athlete{
    associatedtype ShoeSize

    func run(with: ShoeSize)
    func jump(with: ShoeSize)
}

现在我尝试像这样遵循它:

class Person: Athlete{
    func run(with: Int){ // Line A
        print("running")
    }

    func jump(with: String){ // Line B
        print("jumping")
    }
}

A 行,ShoeSize 关联类型被推断为 Int。因此,对于另一个 jump 函数,它期望其输入类型也是 Int。但它得到了String,因此它会抛出错误。

B 行相同。只是这次 ShoeSize 关联类型被推断为String。因此,对于另一个 run 函数,它期望其输入类型也是 String。但它得到了一个Int,因此它会抛出错误。

基本上通过这样做我得到了以下2个错误:

note: matching requirement 'jump(with:)' to this declaration inferred associated type to 'String'
 func jum(with: String){


 note: matching requirement 'run(with:)' to this declaration inferred associated type to 'Int'
 func run(with: Int){

解决办法是什么?

run 和 jump 都应该有相同的输入,因为它们都受相关类型的限制

【讨论】:

    猜你喜欢
    • 2020-05-11
    • 1970-01-01
    • 1970-01-01
    • 2011-07-23
    • 2012-12-18
    • 1970-01-01
    • 1970-01-01
    • 2011-09-07
    相关资源
    最近更新 更多