为什么数组不符合Equatable,当它的项在Swift中是Equatable时?

时间:2017-07-08 17:04:50

标签: arrays swift generics swift3 enums

更新

从Xcode 9.3开始,包括 Swift 4.1 ,数组相等按预期工作,原始问题中的代码编译时没有错误。

然而,请参阅接受的答案,因为它提供了更好,更现代的解决方案。

原始问题如下:

当我尝试声明类型为[Post]的通用枚举的实例时,我收到错误消息

  

类型'[Post]'不符合协议'Equatable'

这是无稽之谈,因为Post符合Equatable并且我实际上可以比较两个[Post]实例而没有编译错误?

在以下示例中,我使用Post扩展了Result<T>Equatable类型,然后我做了一些测试:

  1. 测试我可以比较两种Post类型:确定
  2. 测试我可以比较两种[Post]类型:确定
  3. 测试我可以比较两种Result<Post>类型:确定
  4. 测试我可以比较两种Result<[Post]>类型:错误
  5. import Foundation
    
    struct Post {
        let text: String
    }
    extension Post: Equatable {}
    func ==(lhs: Post, rhs: Post) -> Bool {
        return lhs.text == rhs.text
    }
    
    
    enum Result<T: Equatable> {
        case success(result: T)
        case error
    }
    extension Result: Equatable {
        static func ==(lhs: Result<T>, rhs: Result<T>) -> Bool {
            switch (lhs, rhs) {
            case let (.success(lhsVal), .success(rhsVal)):
                return lhsVal == rhsVal
            case (.error, .error):
                return true
            default:
                return false
        }
    }
    
    func test() {
    
        // Test 1: Check Post type for equality: OK
        let post1: Post = Post(text: "post 1")
        let post2: Post = Post(text: "post 2")
    
        if post1 == post2 {
            print("equal posts")
        }
    
        // Test 2: Check [Post] type for equality: OK
        let arrayOfPosts1: [Post] = [ post1, post2 ]
        let arrayOfPosts2: [Post] = [ post1, post2 ]
    
        if arrayOfPosts1 == arrayOfPosts2 {
            print("equal arrays of post")
        }
    
        // Test 3: Check Result<Post> type for equality: OK
        let result1: Result<Post> = Result<Post>.success(result: post1)
        let result2: Result<Post> = Result<Post>.success(result: post2)
    
        if result1 == result2 {
            print("equal results of post")
        }
    
        // Test 4: Check Result<[Post]> type for equality: ERROR
        // Compiler error: "Type '[Post]' does not conform to protocol 'Equatable'"
        let arrayResult1: Result<[Post]> = Result<[Post]>.success(result: arrayOfPosts1)
        let arrayResult2: Result<[Post]> = Result<[Post]>.success(result: arrayOfPosts2)
    
        if arrayResult1 == arrayResult2 {
            print("equal results of array of posts")
        }
    }
    

3 个答案:

答案 0 :(得分:5)

Swift 4.1更新:

随着Swift 4.1中条件一致性的引入,$public = "My public key"; $private = "My private key"; require_once 'classes/bittrex.php'; $request = new Bittrex($public, $private); $markets = $request->GetMarkets(); $data = json_decode($markets, true); 现在符合Array,因此应该解决问题而无需采用任何变通方法。

此外,Swift现在允许类型自动合成Equatable一致性,只要其所有成员都是Equatable,只需将Equatable一致性声明为原始类型定义的一部分(不是扩展)但没有实现任何要求。这适用于枚举提供的关联值(如果有)为Equatable

现在可以更简洁地编写此问题的代码,如下所示:

Equatable

此代码将通过问题中指定的所有测试:

import Foundation

struct Post: Equatable {
    let text: String
}

enum Result<T>: Equatable where T: Equatable {
    case success(result: T)
    case error
}

这是输出:

func test() {

    // Test 1: Check Post type for equality: OK
    let post1 = Post(text: "post")
    let post2 = Post(text: "post")

    if post1 == post2 {
        print("equal posts")
    }

    // Test 2: Check [Post] type for equality: OK
    let arrayOfPosts1 = [post1, post2]
    let arrayOfPosts2 = [post1, post2]

    if arrayOfPosts1 == arrayOfPosts2 {
        print("equal arrays of post")
    }

    // Test 3: Check Result<Post> type for equality: OK
    let result1 = Result<Post>.success(result: post1)
    let result2 = Result<Post>.success(result: post2)

    if result1 == result2 {
        print("equal results of post")
    }

    // Test 4: Check Result<[Post]> type for equality: OK
    let arrayResult1: Result<[Post]> = Result<[Post]>.success(result: arrayOfPosts1)
    let arrayResult2: Result<[Post]> = Result<[Post]>.success(result: arrayOfPosts2)

    if arrayResult1 == arrayResult2 {
        print("equal results of array of posts")
    }
}

答案 1 :(得分:1)

这个问题非常糟糕,但仍未在Swift 4中修复。

除了ArrayResult

之外,我还专门针对结果数组使用了不同类型的Result来处理这个问题
public enum ArrayResult<T:Equatable> {

    case success(result: [T])
    case failure(error: Error)
}

extension ArrayResult: Equatable {

    public static func ==(lhs: ArrayResult<T>, rhs: ArrayResult<T>) -> Bool {
        switch (lhs) {
        case .success(let lhsResult):
            if case .success(let rhsResult) = rhs, lhsResult == rhsResult { return true }
        case .failure(let lhsError):
            // We cast associated Error to a NSError so we get Equatable behaviour
            // (Apple guarantee that Error can always be bridged to an NSError)
            if case .failure(let rhsError) = rhs, lhsError as NSError == rhsError as NSError { return true }
        }
        return false
    }
}

func test() {

    // Test 4: Check Result<[Post]> type for equality: NOW OK
    let arrayResult1: ArrayResult<Post> = ArrayResult<Post>.success(result: arrayOfPosts1)
    let arrayResult2: ArrayResult<Post> = ArrayResult<Post>.success(result: arrayOfPosts2)

    if arrayResult1 == arrayResult2 {
        print("equal results of array of posts")
    }
}

答案 2 :(得分:-1)

load data;

没有扩展名