简体   繁体   中英

Shouldn't an optional be inclusive to a non-optional type in Swift?

Updated: with full code

I have this code:

struct Set<T : Hashable>: Sequence {
    var items: Dictionary<Int, T> = [:]

    func append(o: T?) {
        if let newValue = o {
            items[newValue.hashValue] = newValue
        }
    }

    func generate() -> TypeGenerator<T> {
        return TypeGenerator ( Slice<T>( items.values ) )
    }
}

and I get the error:

Could not find an overload for 'subscript' that accepts the supplied arguments.

for the line:

items[newValue.hashValue] = newValue

As far as I understand it's because newValue 's type is T rather than T? , meaning that it's not optional. This is because the Dictionary 's subscript for accessing the key/value pair is defined as

subscript (key: KeyType) -> ValueType?

meaning that it can only take an optional as the value. And in my case newValue is not an optional after validating it's not nil .

But isn't an optional inclusive of non-optionals? Isn't a type's optional is the type + nil ?

Why would something that can take everything + nil reject a type that can't be nil ?

Small clarification: the reason I check that o is not nil is to be able to call its hashValue which is not accessible directly from the optional or the unwrapped optional ( o!.hashValue throws a compile error).

I also can't replace the assignment line with

items[newValue.hashValue] = o

because it has validated that o is not an optional worthy of assignment even though it does not allow access to it's hashValue property.

The dictionary is not defined to store an optional value. It is just the assignment operator that accepts an optional value because giving it nil will remove the whole key from the dictionary.

The problem you are having is that you are trying to mutate your property items in a non-mutating method. You need to define your method as mutating:

mutating func append(o: T?) {
    if let newValue = o {
        items[newValue.hashValue] = newValue
    }
}

There is certainly no problem assigning an optional variable to a non-optional value:

var optionalString : String? = "Hello, World"

In the same way, it is perfectly valid to assign a dictionary's key to a non-optional value:

var items : [Int:String] = [:]
items[10] = "Hello, World"

You can then assign the key to nil to remove the key entirely from the dictionary:

items[10] = nil

Also, I think you have a fundamental misunderstanding of what a hashValue is and how to use it. You should not pass the value of hashValue to a dictionary as a key. The dictionary calls hashValue on the value you provide, so you are having the dictionary take the hashValue of the hashValue.

There is no guarantee that a hashValue will be different from all other the hashValues of different values . In other words, the hash value of "A" can be the same hash value as "B". The dictionary is sophisticated enough to handle that situation and still give you the right value for a particular key, but your code is not handling that.

You can indeed store a non-optional value in a Dictionary and in general you can pass an object of type T whenever a T? is expected.

The real problem is that you haven't told the compiler that T is Hashable .

You have to put a type constraint on T , using T: Hashable . You can do this at a class level (I suppose this method lives inside a generic class), like so

class Foo<T: Hashable> {
    var items: Dictionary<Int, T> = [:]
    func append(o: T?) {
        if let newValue = o {
            items[newValue.hashValue] = newValue
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM