My goal is to be able insert new item to an optional set which this set has nil value before. For that reason i created this extension, but it does not work, and still I cannot insert a new item to a nil set.
extension Optional where Wrapped == Set<String> {
func myInsert(_ value: String) -> Self {
if let unwrappedSet: Set<String> = self {
var newSet: Set<String> = unwrappedSet
newSet.insert(value)
return newSet
}
else {
let set: Set<String>? = [value]
return set
}
}
}
use case:
func myTest() -> Set<String>? {
var set: Set<String>? = nil
set.myInsert("Hello")
return set
}
if let set: Set<String> = myTest() {
print(set)
}
I refuse to believe there is issue with my extension, and i think the issue is from xcode itself, look the function below it is same function but outside of extension, it does works!
func myInsert(set: Set<String>?, value: String) -> Set<String>? {
if let unwrappedSet: Set<String> = set {
var newSet: Set<String> = unwrappedSet
newSet.insert(value)
return newSet
}
else {
let set: Set<String>? = [value]
return set
}
}
use case:
let set: Set<String>? = nil
let newSet = myInsert(set: set, value: "Hello")
let newSet2 = myInsert(set: newSet, value: "World")
print(newSet2)
result:
Optional(Set(["Hello", "World"]))
CodePudding user response:
From your test case code:
var set: Set<String>? = nil
set.myInsert("Hello")
you expect the variable set
to change from nil
to a wrapped Set<String>
containing the String
"Hello"
. In order for the value to modify itself, the func
must be mutating
.
Make myInsert
a mutating
func
and assign the new set
to self
. Since myInsert
is mutating
, it doesn't need to return a value which your test is ignoring anyway:
extension Optional where Wrapped == Set<String> {
mutating func myInsert(_ value: String) {
if self == nil {
self = [value]
} else {
self?.insert(value)
}
}
}
Test
func myTest() -> Set<String>? {
var set: Set<String>? = nil
set.myInsert("Hello")
set.myInsert("Goodbye")
return set
}
if let set = myTest() {
print(set)
}
["Hello", "Goodbye"]
Making insert
more usable
(Thanks to @LeoDabus for his suggestions)
We can make this work with a Set
of any type and make it more like the original insert
on Set
by having it return a @discardableResult
containing a tuple with a Bool
indicating if a the value was inserted and the memberAfterInsert
:
extension Optional where Wrapped: SetAlgebra {
@discardableResult
mutating func insert(_ newMember: Wrapped.Element) -> (inserted: Bool, memberAfterInsert: Wrapped.Element) {
if self == nil {
self = .init()
}
return self!.insert(newMember)
}
}
CodePudding user response:
vacawama and Leo's answer is correct but you want to understand why your code doesn't work. I'll try to explain why does not work as you expected. The function you created "myInsert" defines a Set object, inserts the parameter value and returns the newly created Set object. The problem is you are not actually assigning the created Set object to actual Set variable (which is "self") that you are working on.
if let unwrappedSet: Set<String> = self {
var newSet: Set<String> = unwrappedSet // assigning nil valued "self" to a variable doesn't assign the original variable. Assigning nil to another variable makes that variable again nil. Both are equal (they are nil) but not same variables.
newSet.insert(value) // inserts value to second Set object not to "self". So still, your actual Set doesn't contains the given value.
return newSet // returns the value inserted Set object, not "self".
}
else {
let set: Set<String>? = [value]
return set
}
If we look at myTest() function you can see that your adding operation doesn't work on actual variable.
var set: Set<String>? = nil
set.myInsert("Hello") // at this point, myInsert function returns a new Set object with inserted given value. But this operation does not effect the original variable above. That means set variable is still nil
return set // returns nil variable.
I hope this helps.