I want to wipe an input string. Let's start with this:
func foo(s: String) {
s.replaceSubrange(0..<s.characters.count,
with: String(repeating: "0", count: s.characters.count))
}
Predictably, this results in
cannot use mutating member on immutable value: 's' is a 'let' constant
Fine:
func foo(s: inout String) {
s.replaceSubrange(0..<s.characters.count,
with: String(repeating: "0", count: s.characters.count))
}
But now:
'inout String' is not convertible to 'String'
pointing to .character
-- what?!
Oddly enough, when I do:
func foo(s: inout String) {
let n = s.characters.count
s.replaceSubrange(0..<n,
with: String(repeating: "0", count: n))
}
the call to .characters
is completely fine, but
cannot invoke 'replaceSubrange' with an argument list of type '(CountableRange, with: String)'
Using 0...n-1
doesn't work, either.
How can I replace characters in a parameter string?
A String
's CharacterView
isn't indexed by Int
, rather it is indexed by the opaque String.Index
type. Therefore you would want to say:
func foo(s: inout String) {
s.replaceSubrange(s.startIndex..<s.endIndex,
with: String(repeating: "0", count: s.characters.count))
}
We're not directly using the string's characters
property to get the start or end index, as String
provides for them in it's API for convenience – but they are just forwarded onto the CharacterView
.
To replace different subranges, you would want to use the various indexing method, such as index(after:)
, index(before:)
and index(_:offsetBy:)
on String
in order to offset the indices for the lower and upper bound of the range (these methods also just forward onto the string's character view). A good summary of these methods is given in this Q&A.
The reasoning behind not using Int
as the index type is partly due to the fact that subscripting a String
with an Int
would present the illusion that indexing the string is a trivial operation – which is not always the case, due to the fact that characters can have different byte lengths, thus potentially making indexing an O(n) operation.
Although if your aim to just to set s
to a string containing all zeroes with the same count as it's previous value, you could just do an assignment instead:
func foo(s: inout String) {
s = String(repeating: "0", count: s.characters.count)
}