首页 > 解决方案 > 与泛型类型和协议关联的类型约束允许绕过 WHERE 子句

问题描述

为了完全理解问题,我在代码中评论了我的想法。为了便于查看,您可以将粘贴复制到 SWIFT 游乐场。不确定这是错误还是对类型推断的根本误解。我能够通过“欺骗”类型推断来绕过 WHERE 子句,让我发送不同的类型。

    import Foundation

protocol Container {
  associatedtype Item
  mutating func append(_ item: Item)
  var count: Int { get }
  subscript(i: Int) -> Item { get }
}

struct Stack<U>: Container {
  var items = [U]()
  mutating func push(_ item: U) {
    items.append(item)
  }
  mutating func pop() -> U {
    return items.removeLast()
  }
  // conformance to the Container protocol
  mutating func append(_ item: U) {
    self.push(item)
  }
  var count: Int {
    return items.count
  }
  subscript(i: Int) -> U {
    return items[i]
  }
}

struct Devin {} /// This is to describe a custom type.

extension Devin: Container {
  //typealias Item = String

  /// NOTE: Eventhough the typealias is omitted, the type is inferred as String due to the pattern defined here.
  mutating func append(_ item: String) {
    /// do nothing
  }
  var count: Int {
    /// do nothing
    return 0
  }
  subscript(i: Int) -> String {
    /// do nothing
    return "0"
  }
}

/// and here comes the confusing part.......

// Using a PROTOCOL in its ASSOCIATED TYPE'S constraints

protocol SuffixableContainer: Container {
    /// Here we are saying SUFFIX like the ITEM constraint before is a type that conforms to this protocol being defined and the actual constraint is the where clause.
  associatedtype Suffix: SuffixableContainer where Suffix.Item == Item
    /// Suffix has two constraints: It must conform to the SuffixableContainer protocol (the protocol currently being defined)
    /// and its Item type must be the same as the container’s Item type. ( what does it look like when they arent the same? )
    /// to understand whats happening here you have to see how its being used.
    ///
                               /// here is where its getting used and this is the important piece
    func suffix(_ size: Int) -> Suffix
}

extension Stack: SuffixableContainer {
                                /// when conforming
    func suffix(_ size: Int) -> Stack {
        var result = Stack()
        for index in (count-size)..<count {
            result.append(self[index])
        }
        return result
    }
    // Inferred that Suffix is Stack.
}

/// in the commented out code below:
/// Eventhough, returning a stack in the generic form passed, returning one of a different type than the STRING that was inferred will throw an error.
/// Here we can return an instance of Devin and be fine but when we return an instance of a Stack that is a INT type it errors.
//extension Devin: SuffixableContainer {
//  func suffix(_ size: Int) -> Stack<Int> {
//    return Stack<Int>()
//  }
//}
/// Error:  note: requirement specified as 'Self.Item' == 'Self.Suffix.Item' [with Self = Devin]
/// This is the behavior you would expect

/// But here is a different story
/// Return (  Stack = just fine | Devin = just fine | Stack<Devin> = no go my friend )
extension Devin: SuffixableContainer {
  func suffix(_ size: Int) -> Devin {
    print(size)
    return Devin()
  }
}

print(Devin.Suffix.self)
print(Devin.Item.self)
if Devin.Item.self == Devin.Suffix.self {
  print("It Worked")
} else {
  print("They arent the same")
  /// For those who can guess it will print that they aren't the same which violates the where clause
}
let store = Devin.suffix(Devin())
/// Not sure I understand why this works


/// now here is where I can really see the issue.
/// After printing the two types Devin does not equal String &
/// Devin and String are not the same types and the where clause didnt throw any error.
/// And for whatever reason I can pass the Devin object

标签: swift

解决方案


where子句不限制Devin.Suffix为相同,因为Devin.Item它限制Devin.Suffix.Item为与其相同Devin.Item

保持其他所有内容不变,打印以下代码It Worked

print(Devin.Suffix.Item.self)
print(Devin.Item.self)
if Devin.Item.self == Devin.Suffix.Item.self {
  print("It Worked")
} else {
  print("They aren't the same")
}

推荐阅读