[Discussion]: Deprecate !-Unwrapping of Optionals

-1

One word: Playgrounds

-- E

···

On Feb 29, 2016, at 1:01 PM, Patrick Gili via swift-evolution <swift-evolution@swift.org> wrote:

I agree with Brent's position on this. Simply put, there are use cases that forced unwrapping is necessary.

-Patrick

On Feb 29, 2016, at 3:40 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

Because of the above, I'd like to start a discussion about its deprecation and eventual removal in favor of either an unsafeUnwrap function or other extra-syntactic constructs. I don't intend for force-unwrapping as a concept to go away, as there will always be corner cases where it is necessary, but I don't believe it deserves to be so easy to do in a language that holds safety as a core tenant.

I could not disagree more strongly.

Swift takes the position that assertions are good and that it's better to crash in an unexpected state than to limp on. Force unwrapping is an example of that philosophy. A force unwrap is a "this is not nil" assertion.

That assertion often is not appropriate—namely, in code that tests for `nil`—and so we provide, by my count, five general ways to test-and-use nils (pattern matching, optional binding, optional chaining, defaulting, and mapping) without force unwrapping. These features are all great and, with *very* few exceptions, you should always use one of them rather than test-and-force-unwrap.

But where it *is* appropriate to assert non-nil, *we want you to force unwrap*. If, as you write your code, you are certain a value cannot be nil, we want your code to say so. That way, the code will crash if you're wrong, at the exact point where you made the mistake, where the problem will be easiest to diagnose. We do *not* want you to use a test-and-use construct in a place where you believe the test will always succeed, because you will probably not give any particular attention to the test-failed behavior, so it will probably be wrong.

In other words, making force unwrapping more difficult will not make people correctly handle unexpected nils—it will make them write code that *undetectably* does the wrong thing by skipping code that wasn't actually meant to be skipped. You'll see fewer crashes but more bugs. That runs counter to Swift's formulation of safety, and I think it would be a serious mistake.

I recently had the pleasure of debugging some force unwrap crashes in a view controller I hadn't touched in a while. The force unwraps I used very quickly demonstrated to me that my ideas about the code's lifecycle were wrong: for instance, I thought it would never be called before a certain property was set, but it turned out it sometimes was. If I had used a conditional construct without thinking through the consequences, that would have manifested as a mysteriously empty menu which I would have had trouble debugging. By stating explicitly in my code that I believed those values could not be nil, Swift quickly disproved my belief, showed me exactly what was wrong, and prompted me to correct it. That's the best thing it could have done for me in this situation.

I often see Swift style guides suggest that force-unwrapping is a strong code smell and should always be avoided, but I think that's wrongheaded. Test-and-force-unwrap *is* a code smell, and force-unwrap-when-you're-not-really-sure is incorrect too. But when you *are* sure, force unwrap is the very best way to write your code, because it will make any mistakes immediately obvious.

That's a feature, not a bug.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

1 Like

Whoops, I meant "it is never safe in general to dereference arbitrary memory".

~Robert Widmann

2016/02/29 13:50、Developer <devteam.codafi@gmail.com> のメッセージ:

···

The point is there *are* graceful ways of handling this that aren't unwrapping. See all the language features I mentioned in the original. There is really very little excuse to use a dereference operator all the same without a guard, precondition, or other static guarantee, and the language has given you the tools, many times over, to write those kinds of sanity checks.

Which is why I don't follow the logic for keeping ! around. In C and Objective-C, it is _never_ safe to dereference memory, even in cases where you have reasonable certainty about its validity. It seems to me making it more explicit where that logic gap is covered by intuition leads to higher quality code.

~Robert Widmann

2016/02/29 12:08、David Owens II <david@owensd.io> のメッセージ:

On Feb 29, 2016, at 6:54 AM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

I have to clarify here: I am not taking about .!, as!, as?, ?, etc. I am taking about making naked bang-unwrapping more difficult. That is, those cases when you write

let thing = optVal!

These are equivalent to naked unguarded dereferences in C-like languages and are, we can all agree, a code smell, not a feature. One would not ever recommend to a user of such languages that * or -> be used before checking that an object or region of memory was non-NULL. So, too, should we discourage this case by removing postfix-! from the language itself (notice it isn't even an operator, it is actually a part of the language).

I don't agree with this assertion. There are _many_ cases where you don't check for the pointer being null before accessing it. Those are programmer errors when the data is null. If there is no graceful way for the program to handle that case, what's the point of silently failing? Maybe later you come along because something keeps coming up with a null problem that you can't track down so you build something around it to recover.

I also don't see `let thing = unwrap(optVal)` or `let thing = optVal.unwrap()` as significantly better. The functionality still needs to be there, and this version is only slightly easier to grep vs. !.

-David

For starters, the interface builder generates code that uses forced unwrapping.

-Patrick

···

On Feb 29, 2016, at 4:26 AM, Pyry Jahkola via swift-evolution <swift-evolution@swift.org> wrote:

To me, even after soon two years of Swift, it's still extremely hard to visually see where forced unwrapping happens. I do not think we should recommend unsafeUnwrap though because we'd then trade a guaranteed crash for undefined behaviour on nil, which is definitely the wrong choice at least 95% of time.

And for those cases there will be a standard library function or something more obvious than bang to make up for the deprecated operator. Force unwrapping will not go away (that's a terribly unproductive idea), but the goal is to make a dangerous operation more obvious[ly dangerous].

~Robert Widmann

2016/02/29 15:01、Patrick Gili <gili.patrick.r@gili-labs.com> のメッセージ:

···

I agree with Brent's position on this. Simply put, there are use cases that forced unwrapping is necessary.

-Patrick

On Feb 29, 2016, at 3:40 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

Because of the above, I'd like to start a discussion about its deprecation and eventual removal in favor of either an unsafeUnwrap function or other extra-syntactic constructs. I don't intend for force-unwrapping as a concept to go away, as there will always be corner cases where it is necessary, but I don't believe it deserves to be so easy to do in a language that holds safety as a core tenant.

I could not disagree more strongly.

Swift takes the position that assertions are good and that it's better to crash in an unexpected state than to limp on. Force unwrapping is an example of that philosophy. A force unwrap is a "this is not nil" assertion.

That assertion often is not appropriate—namely, in code that tests for `nil`—and so we provide, by my count, five general ways to test-and-use nils (pattern matching, optional binding, optional chaining, defaulting, and mapping) without force unwrapping. These features are all great and, with *very* few exceptions, you should always use one of them rather than test-and-force-unwrap.

But where it *is* appropriate to assert non-nil, *we want you to force unwrap*. If, as you write your code, you are certain a value cannot be nil, we want your code to say so. That way, the code will crash if you're wrong, at the exact point where you made the mistake, where the problem will be easiest to diagnose. We do *not* want you to use a test-and-use construct in a place where you believe the test will always succeed, because you will probably not give any particular attention to the test-failed behavior, so it will probably be wrong.

In other words, making force unwrapping more difficult will not make people correctly handle unexpected nils—it will make them write code that *undetectably* does the wrong thing by skipping code that wasn't actually meant to be skipped. You'll see fewer crashes but more bugs. That runs counter to Swift's formulation of safety, and I think it would be a serious mistake.

I recently had the pleasure of debugging some force unwrap crashes in a view controller I hadn't touched in a while. The force unwraps I used very quickly demonstrated to me that my ideas about the code's lifecycle were wrong: for instance, I thought it would never be called before a certain property was set, but it turned out it sometimes was. If I had used a conditional construct without thinking through the consequences, that would have manifested as a mysteriously empty menu which I would have had trouble debugging. By stating explicitly in my code that I believed those values could not be nil, Swift quickly disproved my belief, showed me exactly what was wrong, and prompted me to correct it. That's the best thing it could have done for me in this situation.

I often see Swift style guides suggest that force-unwrapping is a strong code smell and should always be avoided, but I think that's wrongheaded. Test-and-force-unwrap *is* a code smell, and force-unwrap-when-you're-not-really-sure is incorrect too. But when you *are* sure, force unwrap is the very best way to write your code, because it will make any mistakes immediately obvious.

That's a feature, not a bug.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

It is guarded in the same way that *(NULL) is guarded by an EXC_BAD_ACCESS. It is all partiality in the end and beside the point I wished to make at the root of the discussion.

Bang isn’t going anywhere under the original scheme, its usage is merely getting more obvious. If you’ll excuse the pun, it’s a sin-tax on the usage of bang that I want, nothing more. You can always reach for that hammer should you choose, but you will have to pay for it by using a function. The language shouldn’t encourage thinking with partiality by baking it into its syntax so explicitly.

···

On Feb 29, 2016, at 6:29 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

These are equivalent to naked unguarded dereferences in C-like languages and are, we can all agree, a code smell, not a feature.

I previously wrote a 463-word post in this thread arguing that these are *not* code smells. Maybe you don't agree with that position, but if you really think "we can all agree" it's a code smell, you're not actually listening to the arguments against you.

I also object to the idea that these are "unguarded" dereferences. `!` *is* guarded by code which always reliably causes a crash if the value is nil. If this unwrap is guarded:

  guard let foo = foo else {
    fatalError("foo is nil!")
  }
  doSomething(foo)

Then so is this:

  doSomething(foo!)

--
Brent Royal-Gordon
Architechies

To move the discussion forward, perhaps a better analogy is not pointers, but casting. C++ uses the well-known const_cast, static_cast, and reinterpret_cast, primitives as a sin-tax on the usage of casting. It makes a fundamentally unsafe operation incredibly obvious, and makes the programmer think twice about asking C++ to violate its internal consistency checks in the name of a “leap of logic” made by the programmer. While it is possible to use casting safely, just as it is possible to use bang safely, the language makes it clear it doesn’t wish to encourage a bad pattern. Casting didn’t go away here, casting didn’t get “harder”, but casting in C++ versus C got syntactically heavier and thus makes the programmer pause, if only for a second longer, to consider other options.

···

On Feb 29, 2016, at 6:29 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

These are equivalent to naked unguarded dereferences in C-like languages and are, we can all agree, a code smell, not a feature.

I previously wrote a 463-word post in this thread arguing that these are *not* code smells. Maybe you don't agree with that position, but if you really think "we can all agree" it's a code smell, you're not actually listening to the arguments against you.

I also object to the idea that these are "unguarded" dereferences. `!` *is* guarded by code which always reliably causes a crash if the value is nil. If this unwrap is guarded:

  guard let foo = foo else {
    fatalError("foo is nil!")
  }
  doSomething(foo)

Then so is this:

  doSomething(foo!)

--
Brent Royal-Gordon
Architechies

Like this?
enum OptionalUnwrappingError : ErrorType {
    case unexpectedlyFoundNil
}
enum Optional<T> {
    typealias Wrapped = T
    case None
    case Some(Wrapped)
    ...
    func unwrap() throws -> Wrapped {
        switch self {
        case .None: throw OptionalError.unexpectedlyFoundNil
        case .Some(let value): return value
        }
    }
}

It’s hard to get more obvious than the compiler complaining that you haven’t wrapped a throwing function in a try block

- Dave Sweeris

···

On Feb 29, 2016, at 2:24 PM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

And for those cases there will be a standard library function or something more obvious than bang to make up for the deprecated operator. Force unwrapping will not go away (that's a terribly unproductive idea), but the goal is to make a dangerous operation more obvious[ly dangerous].

I was thinking something more like

func unsafeUnwrap<T>(opt : Optional<T>) -> T {
    switch opt {
        case let .Some(val):
            return val
        default:
             fatalError("Unexpectedly found nil while unwrapping optional value")
    }
}

Bang doesn't go away, it just becomes more obvious that you're making a leap of logic (as it were).

~Robert Widmann

2016/02/29 15:59、davesweeris@mac.com のメッセージ:

···

On Feb 29, 2016, at 2:24 PM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

And for those cases there will be a standard library function or something more obvious than bang to make up for the deprecated operator. Force unwrapping will not go away (that's a terribly unproductive idea), but the goal is to make a dangerous operation more obvious[ly dangerous].

Like this?
enum OptionalUnwrappingError : ErrorType {
    case unexpectedlyFoundNil
}
enum Optional<T> {
    typealias Wrapped = T
    case None
    case Some(Wrapped)
    ...
    func unwrap() throws -> Wrapped {
        switch self {
        case .None: throw OptionalError.unexpectedlyFoundNil
        case .Some(let value): return value
        }
    }
}

It’s hard to get more obvious than the compiler complaining that you haven’t wrapped a throwing function in a try block

- Dave Sweeris

To move the discussion forward, perhaps a better analogy is not pointers, but casting. C++ uses the well-known const_cast, static_cast, and reinterpret_cast, primitives as a sin-tax on the usage of casting. It makes a fundamentally unsafe operation incredibly obvious, and makes the programmer think twice about asking C++ to violate its internal consistency checks in the name of a “leap of logic” made by the programmer. While it is possible to use casting safely, just as it is possible to use bang safely, the language makes it clear it doesn’t wish to encourage a bad pattern. Casting didn’t go away here, casting didn’t get “harder”, but casting in C++ versus C got syntactically heavier and thus makes the programmer pause, if only for a second longer, to consider other options.

To me, the use of an exclamation point for forced unwrapping is a great choice. It suggests caution, just as it does when it appears on warning signs. I already feel apprehensive on the rare occasions when I use forced unwrapping, even though I know it’s the proper choice.

—CK

···

On Feb 29, 2016, at 4:41 PM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 29, 2016, at 6:29 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

These are equivalent to naked unguarded dereferences in C-like languages and are, we can all agree, a code smell, not a feature.

I previously wrote a 463-word post in this thread arguing that these are *not* code smells. Maybe you don't agree with that position, but if you really think "we can all agree" it's a code smell, you're not actually listening to the arguments against you.

I also object to the idea that these are "unguarded" dereferences. `!` *is* guarded by code which always reliably causes a crash if the value is nil. If this unwrap is guarded:

  guard let foo = foo else {
    fatalError("foo is nil!")
  }
  doSomething(foo)

Then so is this:

  doSomething(foo!)

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Random ultra pedantic point, but dereferencing a null pointer in C (or an nil UnsafePointer in Swift) is actually undefined behavior, it is not guarded by a deterministic trap (as it is in Java).

Details here:

-Chris

···

On Feb 29, 2016, at 4:04 PM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

It is guarded in the same way that *(NULL) is guarded by an EXC_BAD_ACCESS. It is all partiality in the end and beside the point I wished to make at the root of the discussion.

-1 for the deprecation.

-Van

···

On Mon, Feb 29, 2016 at 9:41 PM, Developer via swift-evolution < swift-evolution@swift.org> wrote:

To move the discussion forward, perhaps a better analogy is not pointers,
but casting. C++ uses the well-known const_cast, static_cast, and
reinterpret_cast, primitives as a sin-tax on the usage of casting. It
makes a fundamentally unsafe operation incredibly obvious, and makes the
programmer think twice about asking C++ to violate its internal consistency
checks in the name of a “leap of logic” made by the programmer. While it
is possible to use casting safely, just as it is possible to use bang
safely, the language makes it clear it doesn’t wish to encourage a bad
pattern. Casting didn’t go away here, casting didn’t get “harder”, but
casting in C++ versus C got syntactically heavier and thus makes the
programmer pause, if only for a second longer, to consider other options.

> On Feb 29, 2016, at 6:29 PM, Brent Royal-Gordon <brent@architechies.com> > wrote:
>
>> These are equivalent to naked unguarded dereferences in C-like
languages and are, we can all agree, a code smell, not a feature.
>
> I previously wrote a 463-word post in this thread arguing that these are
*not* code smells. Maybe you don't agree with that position, but if you
really think "we can all agree" it's a code smell, you're not actually
listening to the arguments against you.
>
> I also object to the idea that these are "unguarded" dereferences. `!`
*is* guarded by code which always reliably causes a crash if the value is
nil. If this unwrap is guarded:
>
> guard let foo = foo else {
> fatalError("foo is nil!")
> }
> doSomething(foo)
>
> Then so is this:
>
> doSomething(foo!)
>
> --
> Brent Royal-Gordon
> Architechies
>

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

unsafeUnwrap already exists, though with a slightly different meaning (no
check is performed in -O builds). I think it may be turning into an
"unsafelyUnwrapped" property in the future.

Generally I'm +1 on this. I do share others' concerns about things like
playgrounds and xibs.

I suppose the existence of xibs requires us to keep the
ImplicitlyUnwrappedOptional type if we don't want to require people use "if
let" *everywhere*, unless we somehow move the setting of IBOutlets into a
special throwing initializer.

For writing quick code in playgrounds, something like ".unsafelyUnwrapped"
is probably fine, if a bit wordy. Or if, as Dave Sweeris suggests, you make
"unwrap() throws", then playgrounds can just use "try! x.unwrap()" for
unwrapping. (Or did you want to propose removing try! too?)

Jacob

···

On Mon, Feb 29, 2016 at 1:26 PM, Developer via swift-evolution < swift-evolution@swift.org> wrote:

I was thinking something more like

func unsafeUnwrap<T>(opt : Optional<T>) -> T {
    switch opt {
        case let .Some(val):
            return val
        default:
             fatalError("Unexpectedly found nil while unwrapping optional
value")
    }
}

Bang doesn't go away, it just becomes more obvious that you're making a
leap of logic (as it were).

~Robert Widmann

2016/02/29 15:59、davesweeris@mac.com のメッセージ:

On Feb 29, 2016, at 2:24 PM, Developer via swift-evolution < > swift-evolution@swift.org> wrote:

And for those cases there will be a standard library function or something
more obvious than bang to make up for the deprecated operator. Force
unwrapping will not go away (that's a terribly unproductive idea), but the
goal is to make a dangerous operation more obvious[ly dangerous].

Like this?
enum OptionalUnwrappingError : ErrorType {
    case unexpectedlyFoundNil
}
enum Optional<T> {
    typealias Wrapped = T
    case None
    case Some(Wrapped)
    ...
    func unwrap() throws -> Wrapped {
        switch self {
        case .None: throw OptionalError.unexpectedlyFoundNil
        case .Some(let value): return value
        }
    }
}

It’s hard to get more obvious than the compiler complaining that you
haven’t wrapped a throwing function in a try block

- Dave Sweeris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

unsafeUnwrap already exists, though with a slightly different meaning (no
check is performed in -O builds). I think it may be turning into an
"unsafelyUnwrapped" property in the future.

Generally I'm +1 on this. I do share others' concerns about things like
playgrounds and xibs.

I suppose the existence of xibs requires us to keep the
ImplicitlyUnwrappedOptional type if we don't want to require people use "if
let" *everywhere*, unless we somehow move the setting of IBOutlets into a
special throwing initializer.

For writing quick code in playgrounds, something like ".unsafelyUnwrapped"
is probably fine, if a bit wordy. Or if, as Dave Sweeris suggests, you make
"unwrap() throws", then playgrounds can just use "try! x.unwrap()" for
unwrapping. (Or did you want to propose removing try! too?)

The problem with making unwrap() throws is that throwing is for
recoverable errors, but the intended use case for "!" is for situations
where it would be a program bug if unwrapping failed, and bugs are not
recoverable.

···

on Mon Feb 29 2016, Jacob Bandes-Storch <swift-evolution@swift.org> wrote:

Jacob

On Mon, Feb 29, 2016 at 1:26 PM, Developer via swift-evolution < > swift-evolution@swift.org> wrote:

I was thinking something more like

func unsafeUnwrap<T>(opt : Optional<T>) -> T {
    switch opt {
        case let .Some(val):
            return val
        default:
             fatalError("Unexpectedly found nil while unwrapping optional
value")
    }
}

Bang doesn't go away, it just becomes more obvious that you're making a
leap of logic (as it were).

~Robert Widmann

2016/02/29 15:59、davesweeris@mac.com のメッセージ:

On Feb 29, 2016, at 2:24 PM, Developer via swift-evolution < >> swift-evolution@swift.org> wrote:

And for those cases there will be a standard library function or something
more obvious than bang to make up for the deprecated operator. Force
unwrapping will not go away (that's a terribly unproductive idea), but the
goal is to make a dangerous operation more obvious[ly dangerous].

Like this?
enum OptionalUnwrappingError : ErrorType {
    case unexpectedlyFoundNil
}
enum Optional<T> {
    typealias Wrapped = T
    case None
    case Some(Wrapped)
    ...
    func unwrap() throws -> Wrapped {
        switch self {
        case .None: throw OptionalError.unexpectedlyFoundNil
        case .Some(let value): return value
        }
    }
}

It’s hard to get more obvious than the compiler complaining that you
haven’t wrapped a throwing function in a try block

- Dave Sweeris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
-Dave

Proposal: -1
Chris: +1
Brent: +1
Charles: +1

And it's still better than the iconic NullPointerException, because it will mostly happen sooner, making it easier to understand

Pierre

···

Le 1 mars 2016 à 06:58, Chris Lattner via swift-evolution <swift-evolution@swift.org> a écrit :

On Feb 29, 2016, at 4:04 PM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

It is guarded in the same way that *(NULL) is guarded by an EXC_BAD_ACCESS. It is all partiality in the end and beside the point I wished to make at the root of the discussion.

Random ultra pedantic point, but dereferencing a null pointer in C (or an nil UnsafePointer in Swift) is actually undefined behavior, it is not guarded by a deterministic trap (as it is in Java).

Details here:
What Every C Programmer Should Know About Undefined Behavior #1/3 - The LLVM Project Blog

-Chris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Strong -1.

My UI code has hundreds of these force-unwraps, mostly on resource
access like UIImage(named: "something")!, to catch naming mistakes and
the like. Making it a method will increase the visual noise
significantly, and I can't even imagine using let's for this.

In general, I consider ! an assertion, and definitely NOT a code smell.
There are many cases where generic code may legitimately return nil, but
I as a developer know that it will never happen in my particular case.

A.

I’m not sure it is a good idea to make the language syntax more verbose (and less convenient) in an attempt to suggest best practice. If nothing else force-unwrapping is useful in test code and making test code more verbose is definitely a negative.

As mentioned previously in this thread, I think it would be preferable to consider compiler options for warning/restricting its usage in production code - potentially even eventually moving to force-unwrapping being disabled for production code by default.

-Simon

···

On 1 Mar 2016, at 9:30 PM, Andrey Tarantsov via swift-evolution <swift-evolution@swift.org> wrote:

Strong -1.

My UI code has hundreds of these force-unwraps, mostly on resource access like UIImage(named: "something")!, to catch naming mistakes and the like. Making it a method will increase the visual noise significantly, and I can't even imagine using let's for this.

In general, I consider ! an assertion, and definitely NOT a code smell. There are many cases where generic code may legitimately return nil, but I as a developer know that it will never happen in my particular case.

A.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

As mentioned previously in this thread, I think it would be preferable to consider compiler options for warning/restricting its usage in production code - potentially even eventually moving to force-unwrapping being disabled for production code by default.

This sounds like exactly the sort of style concern we've previously decided to leave to linters.

···

--
Brent Royal-Gordon
Architechies

The ! operator is necessary in some contexts when the language (or my imagination) is not powerful enough to prove in swift that a value cannot be nil.

When I use a bang I'm saying that I know it's not nil but cannot prove it to the compiler.

As a simple example: if I add a value to a dictionary, I know I can get it out of the dictionary immediately after and have it not be nil. Swift doesn't let me prove that, though.

Much more complex examples exist in cases such as initialization of complex objects, where a proof would need to cross module or thread boundaries.

While enhancements such as non empty arrays can improve such proofs, It's likely swift will never increase power sufficiently to allow proofs everywhere, and so the ! operator will continue to be required.

Replacing ! with a run-time construction is a step backwards; the compiler and software engineer will find it harder to recognize and reason about if let than ! for cases where nil is impossible.

There's an on-going discussion of this in another thread. I'd suggest taking your input over there instead of resurrecting a nearly 5-year old thread. :slight_smile:

8 Likes