PrePitch: Optional variables should require explicit initialization

The change makes sense to me, but I still think I’m -1 just due to the massive code churn it will cause for what appears to be minor gains.

The fix is easy for our own maintained codebases, but I’m sure many examples, blog posts and stackoverflow posts etc will break as well and may not be updated

2 Likes

This reminds me of the brain teaser: "If you eat a slice of Swiss cheese, where do the holes go?"

It's by baking in the semantics of "nothingness" that the Swift compiler is able to do some pretty interesting things. @mattt wrote an interesting article about Never once.

I apologize, we're getting a little side tracked from @masters3d's pitch. Sorry for hijacking — I think I've made my points and position clear. Still a -1 from me, sorry.

3 Likes

I foresee a long deprecation period where we just diagnose the issue and offer a fixit but still compile just like any other depreciation cycle to give people enough time to update.

1 Like

Still breaks for the warnings-as-errors crowd (I am not one). And i suspect it will produce enough warnings in a reasonably large codebase that they will drown out warnings you may really need to see.

Edit: that does however address the code examples problem since it will still compile

It's not really solvable with a linter. I want this to produce an error in the init, not the declaration:

struct Foo {
  var bar: Bar?
  var qux: Qux?

  init(bar: Bar?) {
    self.bar = bar 
  } // error: variable qux not initialized
}

+1
I think of .none as a value as any other, so it makes sense to always make it explicit that your Optional contains nil. The only situation I would like not to write = nil is when I am forced to use optionals, because I cannot initialize value on init (for example in UIViewController), but that use case should be fixed with property wrappers

3 Likes

Not to mention type inference, which is implicit typing…

While I haven't yet made up my mind on the proposal, I disagree with that specific bit. For instance, it is often the case in coding practice when an empty array denotes a successful operation result with no values, the opposite of which is nil which denotes that the operation hasn't been completed for whatever reason and thus should be retried. The empty array value is then treated the same way as non-empty.

The whole rationale behind nil and optional check enforcement is to avoid treating particular values as 'magic' states that require special handling. When empty array or empty string are checked for specifically, I consider this a code smell.

What about a DefaultInitializer protocol that allows all types to provide a static default value that will be used for uninitialized variables like var foo : Type?

protocol DefaultInitializer {
   static let initialValue : Self
}

extension Optional : DefaultInitializer {
   static let initialValue = .empty
}

extension String : DefaultInitializer {
   static let initialValue = ""
}

extension Array : DefaultInitializer {
   static let initialValue = []
}

That would at least unify the behavior of the Optional type with all other types in this regard even if we decide to only really implement the protocol for the Optional type.

For me the most problematic thing about the current implementation is that there is no way to opt-out for example like this:

@uninitialized var foo: String?
if condition { 
    opt = "bar" 
} else { 
    // error: variable foo not initialized 
}
1 Like

To Elaborate on some of @clayellis's points, it seems relevant to bring up some of the purpose of Optionals.

This is obviously not new information, but one purpose is giving users a way to easily guarantee runtime safety at compile time. It requires being explicit about whether a value is required or can have no value. If something is declared as Optional, having no value is a valid state and has a very specific runtime meaning.

So if this seems like a special case, that's because it is. Things like empty Arrays or Strings have no specific meaning to the compiler or runtime. .none does.

One thing that hasn't really been brought up is the amount of flexibility and brevity implicit .none gives. E.g. subclasses not having to initialized as many properties in their custom initializers.

Previous domains should also probably be mentioned. Class @propertys in Objective-C are implicitly nil, so having similar behavior in Swift helps with knowledge transfer, as well as converting/interfacing-with existing legacy code.

3 Likes

For iOS development, this is really not a safe assumption. Also, is this a pattern we want to go support, where we ignore the lazy loading aspects of the nib system just for developer convenience?

I’ve seen far too many bugs due to these assumptions. Young devs are encouraged enough to ignore nib loading behavior, lets not make it worse by making the language actively lie to the engineer...

Unfortunately "Safe" is not really a term associated Interface Builder :disappointed:

2 Likes

-1

I like it the way things are. Requiring assignment to nil for optional ivars would just be extra noise with little or no gain in clarity. I like the way swiftlint tells me to not do it.

2 Likes

I'm very much +1 on this. Had a thread on this containing some of the pitfalls of implicit initialization with nil a while back: Remove implicit initialization of Optionals?

I missed this other one too.

I’m leaning towards proposing something along the lines of default initialization via property wrappers.

@Default var foo:String // initialized to “”
@Default var foo2: String? // initialized to .some(String())

This would be counterintuitive given that for the past 5 years, var foo2: String? is nil by default.

1 Like

@Default var foo2: String? // initialized to .some(String())

Would be equivalent to

var foo2: String? // = nil
foo2 = String() // optional promotion is another can of worms. 
print(foo2) // .some("")

Just to be clear, are you proposing @Default instead of the proposed change to require explicitly declaring = nil or alongside it?