3

We can extract words from a Swift string like this:

s.enumerateSubstringsInRange(s.characters.indices, options: .ByWords) {
    (w,_,_,_) in print(w!)
}

but the forced unwrapping is generally a code smell. It is there because the first parameter of the callback is a String? (an optional). I've tried several possible strings in order to force the function to pass nil to the callback (e.g. the empty string, and strings with no word characters) but with no luck!

So I was wondering why the callback takes an optional string. Is there something I overlooked? Is it because a mutable string can be passed in and modified concurrently? If so, then would it be acceptable practice, if I know my original string is a constant (defined with let), to do the forced unwrap?

Ray Toal
  • 86,166
  • 18
  • 182
  • 232
  • 1
    Doing a forced unwrap is effectively never okay. Even if you *want* your program to crash when you get `nil`, it's far better to do something like `guard let` unwrap it and throw a fatal error with a useful description of what went wrong. – nhgrif Oct 31 '15 at 20:26
  • That aside, do you get `nil` in the case that you're using `.ByWords` and have a double-space somewhere in your string, by any chance? – nhgrif Oct 31 '15 at 20:28
  • I do avoid `!` at all times, but in this case the idea that _immutable strings might never produce `nil`_ just got me wondering. I could be wrong about this, though, hence the question. And to the second comment: double spaces do not generate `nil`s. Neither does a string full of non-word characters. I am at a loss on how to generate a `nil` for the callback. – Ray Toal Oct 31 '15 at 20:31
  • I'm not sure either. Seems like the only solution would be to ask Apple (in terms of why it's `String?` versus `String`). – nhgrif Oct 31 '15 at 20:35
  • They would know indeed, but I would be happy to just look at the source code myself but I can't find it. I googled NSString source code and Foundation source code. but `enumerateSubstringsInRange` were not in any of the files I found. – Ray Toal Oct 31 '15 at 20:41
  • 1
    Even if you did find it, it'd not be written in Swift. It'd be in Objective-C & C. And the only thing that'd make it optional versus non-optional is the existence of a [nullability annotation](http://stackoverflow.com/a/29401454/2792531): `__nullable`. – nhgrif Oct 31 '15 at 20:43
  • @nhgrif I think he can not use guard there because it is not a loop or a method. He needs to safely unwrap it using if let substring = substring { ... } – Leo Dabus Nov 01 '15 at 01:52
  • @LeoDabus: You *can* use guard in a closure, the `else { return }` case would then return from the closure immediately. – Martin R Dec 13 '15 at 11:53

1 Answers1

2

(The following information is from the response to my question https://forums.developer.apple.com/thread/28272 in the Apple Developer Forum.)

You can pass the .SubstringNotRequired option to enumerateSubstringsInRange(), and then the closure will be called with substring == nil. This option is documented as

NSStringEnumerationSubstringNotRequired

A way to indicate that the block does not need substring, in which case nil will be passed. This is simply a performance shortcut.

Example:

let str = "Hello, playground"  
str.enumerateSubstringsInRange(str.characters.indices,
    options: [.ByWords, .SubstringNotRequired]) {
        substring, substringRange, _, _ in  
        print(substring, substringRange)  
}

Output:

nil 0..<5
nil 7..<17

I think it is safe to assume that substring != nil if the .SubstringNotRequired option is not given.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • This is fascinating and definitely answers the question, but my mind is completely unable to fathom why that ever would be a good idea. If one is enumerating strings, even via some bizarre filter, I would think that you just pass the strings you find to the closure, and don't pass strings you don't find. Why `nil` is ever a useful thing to enumerate I still can't wrap my head around. – Ray Toal Dec 13 '15 at 20:37
  • @RayToal: With the `.SubstringNotRequired` option, the closure is still called for every word, and `subStringRange` is set to its range. As "OOPer" says in the referenced forum thread, that *might* increase performance if you only need the ranges but not the actual strings. – Martin R Dec 13 '15 at 20:42