2

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?

Raphael
  • 9,779
  • 5
  • 63
  • 94
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/137144/discussion-between-raphael-and-hamish). – Raphael Mar 03 '17 at 11:50
  • If your intention is to wipe memory for security reasons, then this might be of interest: [Secure Memory For Swift Objects](http://stackoverflow.com/questions/27715985/secure-memory-for-swift-objects). – Martin R Mar 03 '17 at 12:00
  • @MartinR Useful reference, thanks! – Raphael Mar 03 '17 at 12:52

2 Answers2

5

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)
}
Community
  • 1
  • 1
Hamish
  • 78,605
  • 19
  • 187
  • 280
-1

You need to use Mutable Strings for this purpose. Here is the complete code written in Swift 3.0

var string: NSMutableString = "This is my string."

    func foo(s: NSMutableString)
    {
        s.replaceCharacters(in: NSRange(location:0, length: s.length - 1 ), with: String(repeating: "0", count: s.length))
        print(s)
    }

    foo(s: string) //Function Calling

OutPut: 000000000000000000

Peeyush karnwal
  • 622
  • 7
  • 24
  • 1
    You do not need to use `NSMutableString` here. – Hamish Mar 03 '17 at 12:27
  • If I want to mutate my string then I should. Can you plzz put some code to prove your point. Also, But why not just s = String(repeating: "0", count: s.characters.count)? It will work as we're allocating new memory to our string object. – Peeyush karnwal Mar 03 '17 at 12:33
  • 1
    Swift's `String` is mutable. I gave an example of this in the very comment you quoted – `s.replaceSubrange(s.startIndex.. – Hamish Mar 03 '17 at 12:44
  • YEs you're right but when we pass a String to a function, then that receiving parameter will become immutable. var string: String = "This is my string." func foo(s: String) { s.replaceSubrange(s.startIndex.. – Peeyush karnwal Mar 03 '17 at 12:51
  • 1
    @Peeyushkarnwal You need to note and then read about the `inout` keyword. – Raphael Mar 03 '17 at 12:52