最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

generics - Swift custom struct: string interpolation not picking up custom description - Stack Overflow

programmeradmin6浏览0评论

In the following Swift code:

import Foundation

struct MySet<T: Hashable>: CustomStringConvertible {
    let set: Set<T>
}

extension MySet {
    var description: String {
        return "\(self.set)"
    }

}

extension MySet where T: Comparable {
    var description: String {
        return "\(self.set.sorted())"
    }
}

let intSet: Set<Int> = [6, 3, 4, 9, 12, 49, -1, 44, -1000, 1000]
let myIntSet = MySet(set: intSet)
print("myIntSet: \(myIntSet.description)")
print("myIntSet: \(myIntSet)")
print("myIntSet: ", String(describing: myIntSet))

I'm trying to create a custom description (for the CustomStringConvertible protocol) that is different depending on whether the elements of the set are Comparable. The output of the above is:

myIntSet: [-1000, -1, 3, 4, 6, 9, 12, 44, 49, 1000]
myIntSet: [9, -1, 3, 1000, 6, 4, 12, 44, 49, -1000]
muIntSet:  [9, -1, 3, 1000, 6, 4, 12, 44, 49, -1000]

As you can see, if I explicitly print myIntSet.description, I get the desired output, which is a sorted list. But if I use string interpolation on the struct itself, or String(describing: myIntSet), I get the description from the first (unconditional) extension.

Can anyone explain why this is happening? How do I get string interpolation and String(describing: ...) to use the desired description?

In the following Swift code:

import Foundation

struct MySet<T: Hashable>: CustomStringConvertible {
    let set: Set<T>
}

extension MySet {
    var description: String {
        return "\(self.set)"
    }

}

extension MySet where T: Comparable {
    var description: String {
        return "\(self.set.sorted())"
    }
}

let intSet: Set<Int> = [6, 3, 4, 9, 12, 49, -1, 44, -1000, 1000]
let myIntSet = MySet(set: intSet)
print("myIntSet: \(myIntSet.description)")
print("myIntSet: \(myIntSet)")
print("myIntSet: ", String(describing: myIntSet))

I'm trying to create a custom description (for the CustomStringConvertible protocol) that is different depending on whether the elements of the set are Comparable. The output of the above is:

myIntSet: [-1000, -1, 3, 4, 6, 9, 12, 44, 49, 1000]
myIntSet: [9, -1, 3, 1000, 6, 4, 12, 44, 49, -1000]
muIntSet:  [9, -1, 3, 1000, 6, 4, 12, 44, 49, -1000]

As you can see, if I explicitly print myIntSet.description, I get the desired output, which is a sorted list. But if I use string interpolation on the struct itself, or String(describing: myIntSet), I get the description from the first (unconditional) extension.

Can anyone explain why this is happening? How do I get string interpolation and String(describing: ...) to use the desired description?

Share Improve this question edited Mar 24 at 22:13 Rick Clark asked Mar 24 at 19:22 Rick ClarkRick Clark 451 silver badge7 bronze badges 3
  • 1 Not related to your question but initializing a new array before sorting it is pointless. You can use sort method directly on your set. var description: String { self.set.sorted().description } – Leo Dabus Commented Mar 24 at 20:33
  • I believe you need to look into the StringInterpolationProtocol and related protocols, at least for the string interpolation part. I added mutating func appendInterpolation(_ value: MySet<Int>) { appendInterpolation((value.description)") } in an extension to String.StringInterpolation and now the string interpolation example also work but as you can see it's a very simplified solution since the generic type is hardcoded. – Joakim Danielson Commented Mar 24 at 21:05
  • Thanks. I'd prefer not to have to write extensions to any other types. – Rick Clark Commented Mar 24 at 22:10
Add a comment  | 

1 Answer 1

Reset to default 2

Every protocol requirement can only have one witness. In this case the witness for CustomStringConvertible.description is the one declared in the first MySet extension. The description declared in the second extension only hides the description declared in the first extension, and is not a witness to the protocol requirement.

When you access description on a comparable MySet, you cannot access the hidden implementation, but when description is accessed through the CustomStringConvertible protocol (indirectly by string interpolation or String.init(describing:)), only the protocol witness can be accessed.

This means that all the behaviour you want must be put in the description declared in the first extension. For example, you can add your own protocol that allows you to provide a custom description, then check if self conforms to that protocol in description.

protocol CustomSetDescribable {
    var customDescription: String { get }
}

struct MySet<T: Hashable>: CustomStringConvertible {
    let set: Set<T>
}

extension MySet {
    var description: String {
        (self as? any CustomSetDescribable)?.customDescription ?? "\(self.set)"
    }

}

extension MySet: CustomSetDescribable where T: Comparable {
    var customDescription: String { "\(set.sorted())" }
}
发布评论

评论列表(0)

  1. 暂无评论