Sequence.removeFirst() -> Element, which removes the first elements,
Sequence.removeFirst(_ n: Int), which removes the first n elements, but doesn't return them as a SubSequence. I think part of the issue here is the SubSequence got moved down to Collection, but I'm not sure.
I'm trying to fix this by implementing my own extension:
Untitled 5.swift:3:16: error: referencing instance method 'removeFirst' on 'Collection' requires the types 'Self' and 'Self.SubSequence' be equivalent
defer { self.removeFirst(n) }
^
Swift.Collection:1:11: note: where 'Self' = 'Self', 'Self.SubSequence' = 'Self.SubSequence'
extension Collection where Self == Self.SubSequence {
^
I don't see why this requirement exists. What stops me from doing this?
var s: String = "ABCDEF"
let firstThree: Substring = s.removeAndReturnFirst(3)
assert(firstThree == "ABC")
assert(s == "DEF")
If I add the constraint it's bugging me about, then I can only operate on collections where Self == Self.SubSequence, such as Substring, and ArraySlice, but not String or Array.
extension Collection where Self == Self.SubSequence {
mutating func removeAndReturnFirst(_ n: Int) -> SubSequence {
defer { self.removeFirst(n) }
return self.prefix(n)
}
}
var s: Substring = "ABCDEF" // I'm forced to use Substring here
let firstThree: Substring = s.removeAndReturnFirst(3)
assert(firstThree == "ABC")
assert(s == "DEF")
Can somebody please explain:
Why I'm forced to add this constraint?
Why Sequence.removeFirst(_ n: Int) wasn't design to return a subsequence?
Because the implementation takes an appropriate SubSequence and assign it to self.
As you said, SubSequence belongs to Collection, not Sequence as per SE-0234. Also, I couldn't find removeFirst, only dropFirst.
Edit:
Re-reading the post, it seems you're thinking about Collection, not Sequence. If so, the new-answer would be:
Again, because the implementation requires that you can assign a SubSequence to self.
Returning the removed element would require another construction of SubSequence, which you can already get using Collection.prefix(_:), and it doesn't seem to save you anything to do it within removeFirst function.
Yea, looking at your post, it seems you're indeed looking for RangeReplaceableCollection. Swift does have a lot of protocols in Sequence/Collection hierarchy, it does help to familiarize those.
Beware this is a very inefficient extension (on any COW Collection type, not just String) and I wouldn't recommend it.
What is going to happen here is self.prefix is going to create a Substring which holds a deferred copy of the outer string. So on return the string buffer will be multiply referenced. Then the deferred removeFirst mutates this multiply-referenced string buffer, triggering the actual copy. This means mutating the string will be not just O(n) but also allocating.
But outside of this, I think you should reexamine your approach to use subsequences in general. It is probably worth considering a redesign where you instead start with a Substring (created with mystr[...]) and then eat from the front of that substring. This is better not just because it avoids the O(n) nature of removeFirst (because removing the front of a substring is just a question of moving the index of the starting bound). I think the objc.io folks have some material on this approach with strings that's worth taking a look at.