Swift 在泛型函数where子句中使用Self时出错

Swift 在泛型函数where子句中使用Self时出错,swift,generics,swift4,swift-protocols,Swift,Generics,Swift4,Swift Protocols,当我试图将Self作为协议的一部分的泛型函数中的where子句的一部分时,遇到了一个问题 例如,假设我定义了此协议和此通用函数: protocol Animal { associatedtype FoodSource func eat(_ food:FoodSource) } // The where clause specifies that T2 must conform to // whatever type is T1's FoodSource associated t

当我试图将
Self
作为协议的一部分的泛型函数中的
where
子句的一部分时,遇到了一个问题

例如,假设我定义了此协议和此通用函数:

protocol Animal {
    associatedtype FoodSource
    func eat(_ food:FoodSource)
}

// The where clause specifies that T2 must conform to
// whatever type is T1's FoodSource associated type 
func feed<T1: Animal, T2>(animal:T1, food:T2) where T2 == T1.FoodSource {
    animal.eat(food)
}
到目前为止还不错

但是,当我将相同的泛型模式作为协议的一部分实现时,它就不再起作用了:

protocol Food {
    func feed<T:Animal>(to:T) where Self == T.FoodSource
}

extension Food {
    func feed<T:Animal>(to animal:T) where Self == T.FoodSource {
        animal.eat(self)
    }
}

class SteakSalad : Food, Meat, Vegetable {}

SteakSalad().feed(to: Lion())
有什么方法可以达到你想要的行为吗?

在讨论这个问题之前,我强烈建议你重新思考你的问题,简化你的类型。一旦你在Swift中走上混合泛型和协议的道路,你将不停地与类型系统作斗争。部分原因是复杂类型很复杂,即使使用非常强大的类型系统,也很难使它们正确。部分原因是Swift没有一个非常强大的类型系统。当然,与Objective-C或Ruby相比,它的功能非常强大,但它在泛型类型方面仍然相当弱,并且有许多概念无法表达(没有更高级的类型,没有表达协变或逆变的方法,没有依赖类型,还有一些奇怪的怪癖,比如协议并不总是符合它们自己). 几乎在我与开发人员一起处理复杂类型的每一个案例中,他们的实际程序都不需要这么复杂。具有相关类型的协议应被视为高级工具;除非你真的需要,否则不要伸手去拿。更多信息请参见

这不起作用,因为它违反了您的
where
子句:

func feed<T:Animal>(to:T) where Self == T.FoodSource
因此,
Self
SteakSalad
Lion.FoodSource
Meat
。这些不相等,所以这不适用。你真正的意思是:

func feed<T:Animal>(to animal:T) where Self: T.FoodSource
然后让狮子吃肉:

class Lion : Animal {
    typealias FoodSource = Meat
我的游乐场。游乐场:1:15:注:可能有意匹配的“Lion.FoodSource”(又名“Meat”)与“Food”不符

typealias FoodSource=肉类

肉不符合食物吗?哈,不。这是Swift中更大的“协议不符合自身”限制的一部分。你不能像对待继承一样对待协议。有时他们会,有时他们不会

你能做的就是把肉喂给肉食者:

protocol Meat {}

extension Meat {
    func feed<T:Animal>(to animal:T) where T.FoodSource == Meat {
        animal.eat(self)
    }
}
protocol-Meat{}
延伸肉{
func饲料(给动物:T),其中T.FoodSource==肉{
动物。吃(自己)
}
}
蔬菜可以喂给素食者:

protocol Vegetable {}

extension Vegetable {
    func feed<T:Animal>(to animal:T) where T.FoodSource == Vegetable {
        animal.eat(self)
    }
}
协议蔬菜{}
扩展蔬菜{
func饲料(给动物:T),其中T.FoodSource==蔬菜{
动物。吃(自己)
}
}

但据我所知,没有办法在具有关联类型(PAT)的协议上实现这种通用性。这对Swift型系统来说太多了。我的建议是去掉PAT,只使用泛型。这些问题中的大多数都会消失。即使像Scala这样的语言有更强大的类型系统,也有关联的类型,正确的答案通常是更简单的泛型(通常不是这样;我们经常在不需要的时候让事情泛型化)。

也许我很笨,但我不确定这到底是如何应用到我的案例中的。我知道提供的示例在内容上非常相似,但这似乎是一个单独的问题。对于链接帖子,类型推断不再适用于
Cow
,因为没有该方法签名,无法确定
Cow.Food
的关联类型。在我的示例中,我不理解为什么不能直接从提供的参数推断类型(在本例中为
Lion
)。感谢您的详细回复。关于一致性(即
Self:T.FoodSource
)与平等性(
Self==T.FoodSource
)之间的差异,这仍然是一个混乱的根源。如果你看看我的例子,在通用函数中,它似乎打破了这个规则
ChickenSalad
被接受为
Rabbit
Lion
的食物,即使这两个类的类型不完全匹配
T.FoodSource
。这是因为该函数不调用
Self
。因此
ChickenSalad
被解释为
Meat
vegeture
,以使这些类型起作用。原则上,可以对
Self
(或类似的东西)执行类似的操作,但这超出了编译器的类型引擎。关于协议(尤其是PAT),有很多东西“我可以在纸上计算出它是如何工作的”是不够的;编者就是处理不了。在Swift中,泛型函数总是比泛型方法灵活得多。随着时间的推移,这已经变得更好了,但这仍然是事实。谢谢你的澄清。是的,我认为我达到了类型系统的极限,但了解这些极限到底是什么是为什么是有帮助的。
protocol Food {}
protocol Meat: Food {}

protocol Animal {
    associatedtype FoodSource: Food
}
class Lion : Animal {
    typealias FoodSource = Meat
protocol Meat {}

extension Meat {
    func feed<T:Animal>(to animal:T) where T.FoodSource == Meat {
        animal.eat(self)
    }
}
protocol Vegetable {}

extension Vegetable {
    func feed<T:Animal>(to animal:T) where T.FoodSource == Vegetable {
        animal.eat(self)
    }
}