Search code examples
swiftconditional-operator

Why doesn't Swift nil-coalescing ternary operator return unwrapped type?


I read that the ternary operator ?? unwraps an optional if it is not nil, but if I do:

var type: String?
type = "milk"
let certainType = type ?? "melon"

then certainType will still be a String?, and if I do

println("it's a \(certainType)")

it will print:

it's a Optional("milk")

Thoughts?

Update:

Sorry for the confusion - I meant var type: String?

I get that it should print "it's a milk", but what I saw in console is a "it's a Optional("milk")" - anyone else experienced the same issue? Could that be caused by string interpolation?

Asked by @Antonio, here is more context and real code and logging snapshot - type is from Note, which is a NSManagedObject class used to deal with xcdatamodel

class Note: NSManagedObject {
  @NSManaged var type: String?
}

And I have the type set to 'todo' at some point, then have the following code to print them out:

println("type class:\(_stdlib_getDemangledTypeName(note.type))")
let type1 = note.type ?? "note"
println("type1:\(type1)")
let type2: String = note.type ?? "note"
println("type2:\(type2)")

And the output:

type class:Swift.Optional
type1:Optional("todo")
type2:todo

As you can see, if I don't explicitly mark the type1's type as String, it will print undesired result Optional("todo") - I use the type in a string interpolation to construct a path so it matters


Solution

  • The OP asserts that code similar to this:

    var type: String? = "milk"
    let certainType = type ?? "melon"
    println("it's a \(certainType)")
    

    prints an unexpected string:

    "it's a Optional("milk")"

    whereas it should be:

    "it's a milk"

    It turns out that happens when the variable is actually a property with the @NSManaged attribute.

    I suspect that there is a bug in type inference. The OP states that:

    let certainType = type ?? "melon"
    

    prints the wrong result, whereas:

    let certainType: String = type ?? "melon"
    

    prints the correct one.

    So for some reason, without explicitly indicating the variable type, the nil coalescing operator is returning an optional.

    If I change the type of the type variable to either AnyObject or AnyObject?, it actually prints the unexpected result:

    var type: AnyObject = "milk"
    let certainType = type ?? "melon"
    println("it's a \(certainType)")
    

    "it's a Optional(milk)"

    My guess is: because the @NSManaged attribute is used, the property is inferred with the wrong type (AnyObject?) when used, unless the correct type is explicitly indicated.

    As to why that happens, no idea (besides thinking it's a bug)