Etan
Etan

Reputation: 17554

Convert subsequence of Collection to String

I'd like to convert a SubSequence of a Collection to a String. For example, this function collects the first two bytes of the collection and converts it to a String.

func convert<T: Collection>(_ c: T) -> String 
    where T.Iterator.Element == UInt8 
{
    let start = c.startIndex
    let end = c.index(after: start)
    return String(bytes: c[start ... end], encoding: String.Encoding.utf8)!
}

This leads to this error:

error: ambiguous reference to member 'subscript'
        return String(bytes: c[start ... end], encoding: String.Encoding.utf8)!
                             ~^~~~~~~~~~~~~~~
Swift.Collection:167:12: note: found this candidate
    public subscript(position: Self.Index) -> Self.Iterator.Element { get }
           ^
Swift.Collection:189:12: note: found this candidate
    public subscript(bounds: Range<Self.Index>) -> Self.SubSequence { get }
           ^
Swift.Collection:25:12: note: found this candidate
    public subscript(bounds: Range<Self.Index>) -> Slice<Self> { get }
           ^
Swift.IndexableBase:54:12: note: found this candidate
    public subscript(position: Self.Index) -> Self._Element { get }
           ^
Swift.IndexableBase:63:12: note: found this candidate
    public subscript(bounds: Range<Self.Index>) -> Self.SubSequence { get }
           ^
Swift.Indexable:23:12: note: found this candidate
    public subscript(bounds: ClosedRange<Self.Index>) -> Self.SubSequence { get }
           ^
Swift.Indexable:23:12: note: found this candidate
    public subscript(bounds: CountableRange<Self.Index>) -> Self.SubSequence { get }
           ^
Swift.Indexable:45:12: note: found this candidate
    public subscript(bounds: CountableClosedRange<Self.Index>) -> Self.SubSequence { get }

What am I missing here? :-)

Upvotes: 1

Views: 1670

Answers (1)

Hamish
Hamish

Reputation: 80901

Currently, a Collection's SubSequence is not guaranteed to have the same element type as the collection itself, which is due to a limitation of associatedtypes.

In fact, one of the motivations for SE-0142: Permit where clauses to constrain associated types is to allow the constraint associatedtype SubSequence : Sequence where SubSequence.Iterator.Element == Iterator.Element, which would enforce this relationship.

Although in this particular case, as you're not utilising the T.Iterator.Element == UInt8 constraint in the first place, you can constrain T.SubSequence.Iterator.Element instead (thanks @MartinR):

func convert<T: Collection>(_ c: T) -> String
    where T.SubSequence.Iterator.Element == UInt8 {

    let start = c.startIndex
    let end = c.index(after: start)

    // please consider handling the case where String(bytes:encoding:) returns nil.
    return String(bytes: c[start ... end], encoding: String.Encoding.utf8)!
}

(In more general cases where you also need T.Iterator.Element constrained to a given type, you would want to add T.SubSequence.Iterator.Element == T.Iterator.Element as an additional constraint).

Upvotes: 3

Related Questions