Swift enum's unwrapping by reference - swift

I'm new to Swift and decided to write my own Optional enum.
enum MyOptional<Type> {
case none
case some(Type)
func get() -> Type {
switch self {
case .some(let x):
return x
case .none:
fatalError()
}
}
}
var test: MyOptional<String> = MyOptional.some("testString")
test.get().append("#")
But in case I put some struct that have mutating function and call that function - the compiler, obviously, tells me:
error: cannot use mutating member on immutable value: function call
returns immutable value test.get().append("#")
How does Swift's Optional returns struct by reference unwrapping them?

The Swift compiler has quite a bit of built-in support for Optional; including for the postfix operators ! and ? which can produce l-values (values which reside at known locations in memory; therefore allowing for mutations of that memory if the expression is mutable).
Unfortunately I don't believe it's possible to implement your own l-value returning operators (or functions in general), although constructs that allow you to define getters and setters (such as computed properties and subscripts) can be treated as l-values when they have setters:
enum MyOptional<Type> {
case none, some(Type)
var forceUnwrapped: Type {
get {
switch self {
case .some(let x):
return x
case .none:
fatalError()
}
}
set {
self = .some(newValue)
}
}
// just for demonstration; don't actually implement this as a subscript!
subscript() -> Type {
get {
switch self {
case .some(let x):
return x
case .none:
fatalError()
}
}
set {
self = .some(newValue)
}
}
}
var test = MyOptional.some("testString")
test.forceUnwrapped.append("#")
test[].append("#")
Here, test.forceUnwrapped and test[] can be treated as l-values. When mutating through them, the compiler will create a temporary variable from calling the getter, mutate this temporary, and then call the setter with the mutated value.
Although it's worth noting in both cases that when used with assignment (i.e test.forceUnwrapped = ... & test[] = ...), the getter won't be called; only the setter, which gives them slightly different semantics to Optional's postfix !, which will crash on the optional being nil even on assignment (i.e someOptional! = ...).
As an alternative, you could also define a method that takes a closure with an inout parameter, allowing the caller to mutate the force-unwrapped value:
enum MyOptional<Type> {
case none
case some(Type)
mutating func forceMutate<R>(_ body: (inout Type) throws -> R) rethrows -> R {
switch self {
case .some(var x):
defer {
self = .some(x)
}
return try body(&x)
case .none:
fatalError()
}
}
}
var test = MyOptional.some("testString")
test.forceMutate { $0.append("#") }

You can use non-mutating operations, and reassign the result back into the variable:
enum MyOptional<Type> {
case none
case some(Type)
func forceUnwrap() -> Type {
switch self {
case .some(let x):
return x
case .none:
fatalError()
}
}
static func ?? (lhs: MyOptional, rhs: #autoclosure () -> Void) {
}
}
var test: MyOptional<String> = .some("testString")
print(test)
test = .some(test.forceUnwrap() + "#")
print(test)
It might also be useful to have functions like map and flatMap:
extension MyOptional {
func map(_ transform: (Wrapped) -> Wrapped) -> MyOptional<Wrapped> {
switch self {
case .some(let x):
return .some(transform(x))
case .none:
return .none
}
}
mutating func mapInPlace(_ transform: (Wrapped) -> Wrapped) {
self = self.map(transform)
}
}
test = test.map{ $0 + "#" }
print(test)
test.mapInPlace{ $0 + "#" }
print(test)

Related

Operator or function to simplify enum associated value in Swift

I cannot figure out if there is a way to convert an enum case into one of it's associated values or returning a nil in any kind of generic way that could be applied to an enum. It seems like it should be possible at least theoretically.
What I want to do..
let value: TestClass1? = model .> TestEnum.one
// do something
What I am doing now..
if case .one(value: let value) = self.model {
// do something
}
guard case .one(value: let value) = self.model else {
//handle case where self.model != .one
return
}
//do something
Does not compile:
infix operator .>
func .><T,U>( _expression: T, _case: (U) -> T) -> U? {
guard case _case(let u) = _expression else { return nil }
return u
}
Test case:
class TestClass1 {}
class TestClass2 {}
enum TestEnum {
case one(value: TestClass1)
case two(value: TestClass2)
}
struct TestStruct {
let model: TestEnum = .one(value: TestClass1())
func function() -> TestClass1? {
// what I want to do
return model .> TestEnum.one
}
}
If anyone has any ideas on how to make the above operator function work I'd love any suggestions.

Filtering an array based on a Swift enum with an associated value - w/o mentioning the associated value

I have an enum with associated value for some of the cases:
enum Foo {
case a
case b
case c(String?)
}
I also have a struct with this enum as a variable
struct Bar {
var foo: Foo
}
I then have an array of this objects
let array:[Bar] = [Bar(foo: .a), Bar(foo: .c(nil)), Bar(foo: .c("someString"))]
I want to create a function that operates on a subset of this array, based on the cases it receives something like
func printOnly(objectsWithCase: Foo)
So far its pretty simple, but now here's the catch: for this operation I WANT TO IGNORE the associated value.
I would like to make this function be able to take .c case without mentioning an associated value, as if to say "give me the ones with .c regardless of the associated values".
I other words - I'd like to pass in something like .c(_) (this doesn't work of course) and have it return (print in this case) both Bar(foo: .c(nil)) and Bar(foo: .c("someString"))
So far, I only came up with changing the functions declaration to take the filtering closure instead of the cases like this:
func printArray(array: [Bar], condition: (Bar) -> Bool) {
let tmp = array.filter(condition)
print(tmp)
}
I'm wondering if there's a way to do this in Swift, while passing the cases and not the condition block ?
You can use the underscore as a wild card in pattern matching operations:
array.filter {
switch $0.foo {
case .a: return true // keep a
case .b: return false // reject b
case .c(_): return true // keep c, regardless of assoc. value.
}
}
While this is not technically what you ask for (I don't think there's any way to achieve this with enums), you can write a "fake" enum that contains a wildcard c that will match anything you want. This will give you the exact same syntax.
1) Replace Foo with the following
struct Foo: Equatable {
let rawValue: String
let associatedObject: String?
let isWildcard: Bool
fileprivate init(rawValue: String, associatedObject: String?, isWildcard: Bool) {
self.rawValue = rawValue
self.associatedObject = associatedObject
self.isWildcard = isWildcard
}
static var a: Foo {
return Foo(rawValue: "a", associatedObject: nil, isWildcard: false)
}
static var b: Foo {
return Foo(rawValue: "b", associatedObject: nil, isWildcard: false)
}
static var c: Foo {
return Foo(rawValue: "c", associatedObject: nil, isWildcard: true)
}
static func c(_ value: String?) -> Foo {
return Foo(rawValue: "c", associatedObject: value, isWildcard: false)
}
}
func ==(left: Foo, right: Foo) -> Bool {
// Match rawValue + associatedObject unless we have a wildcard
return (left.rawValue == right.rawValue)
&& (left.associatedObject == right.associatedObject || left.isWilcard || right.isWildcard)
}
2) Implement your printOnly function with ==
func printOnly(objects: [Bar], with match: Foo) {
objects.filter { $0.foo == match }.forEach { print($0) }
}
3) Success
printOnly(objects: array, with: .c) // [.c(nil), .c("someString")]
Discussion
The main drawback of this method, besides the additional boilerplate code, is that you are forced to create an enum value that should not be allowed. This method puts the responsibility on you to use it only as a wildcard, and not as a real enum value. It will also not guarantee you that other enum cases cannot be created, although you should be able to mitigate that by making the only initializer fileprivate.
Otherwise, this gives you exactly the same interface and features an enum would give you, you can define your cases just as before
let array = [Bar(foo: .a), Bar(foo: .c(nil)), Bar(foo: .c("Hello")]
Finally, you can still use it inside a switch, except you will always need to add a default statement.
switch Foo.c("Hello") {
case .a:
print("A")
case .b:
print("B")
case .c: // will match .c(nil) and .c("someString")
print("C")
default:
break
}
//: Playground - noun: a place where people can play
enum Foo {
case a
case b
case c(String?)
}
struct Bar {
var foo: Foo
}
let array:[Bar] = [Bar(foo: .a), Bar(foo: .c(nil)), Bar(foo: .c("someString"))]
func printArray(array: [Bar], condition: (Bar) -> Bool) {
let tmp = array.filter(condition)
print(tmp)
}
printArray(array: array) { bar in
switch bar.foo {
case .c:
return true
default:
return false
}
}
or
printArray(array: array) { bar in
if case let Foo.c = bar.foo {
return true
}
return false
}
EDIT
//: Playground - noun: a place where people can play
enum Foo: Equatable {
case a
case b
case c(String?)
}
func ==(lhs: Foo, rhs: Foo) -> Bool {
switch (lhs, rhs) {
case (.a, .a), (.b, .b), (.c, .c):
return true
default:
return false
}
}
struct Bar {
var foo: Foo
}
let array:[Bar] = [Bar(foo: .a), Bar(foo: .c(nil)), Bar(foo: .c("someString"))]
func printArray(array: [Bar], condition: (Bar) -> Bool) {
let tmp = array.filter(condition)
print(tmp)
}
func printOnly(objectsWithCase wantedCase: Foo) {
printArray(array: array) { bar in
if wantedCase == bar.foo {
return true
} else {
return false
}
}
}
printOnly(objectsWithCase:.c(nil))

Assign a Type directly to a Type wrapper in Swift

I have a generic type wrapper which allows you to annotate a value as "absolute" or "relative":
enum Value<Wrapped> {
case .absolute(Wrapped)
case .relative(Wrapped)
}
func printValue(_ value: Value<Int>) {
switch value {
case .absolute(let value): print(value)
case .relative(let value): print(value + 10)
}
}
printValue(.absolute(5)) //5
printValue(.relative(5)) //15
Is there anyway to define Value<Wrapped> in such a way that you can directly assign an instance of Wrapped and it will infer the correct case? For example:
printValue(5) //5
printValue(.relative(5)) //15
Swift Optionals appear to do this:
func printValue(_ value: Optional<Int>) {
switch value {
case .none: print("nil")
case .some(let value): print(value)
}
}
printValue(5) //5
Can I achieve this same behaviour in my enum? Or is Optional achieving this through compiler magic?
AFAIK, Optional has special language support. You can still have a printValue overload that just calls out the other one.
func printValue(_ value: Int) {
printValue(.absolute(value))
}

Is it possible to change an associated value within an enum?

I'm playing around with Swift enums and wondering if there is a way to change the assoicated value of an enum such as the code below attempts but fails.
enum myEnum {
case SomeCase(Int?)
mutating func someFunc() {
switch self {
case .SomeCase(let a):
if a != nil {
// a = 5 can't do this of course as assigning to let
var temp = a!; // but this generates a warning
temp = 5;
}
}
}
}
If this isn't currently possible, will it be when mutating enums come to XCode?
The whole enum is a single value, so you have to replace it entirely, not field by field. But you can certainly do that by replacing self.
enum MyEnum {
case SomeCase(Int?)
mutating func someFunc() {
switch self {
case .SomeCase(_):
self = .SomeCase(5)
}
}
}
// You have to use `var` here of course or you can't mutate it.
var x = MyEnum.SomeCase(1) // .SomeCase(Optional(1))
x.someFunc() // .SomeCase(Optional(5))
Rob Napier's answer is fine but I have a different understanding of your question. I would have written something like this instead:
enum MyEnum {
case SomeCase(Int?)
mutating func someFunc() {
switch self {
case .SomeCase(.Some(_)):
self = .SomeCase(5)
default:
// or case .SomeCase(.None): if you prefer
break
}
}
}
With this code, the enum is mutated with someFunc if and only if its associated value is not nil. Thus, I have the following results while testing in a Playground (results appear as comments):
var x = MyEnum.SomeCase(nil)
switch x {
case .SomeCase(.Some(let a)):
println(a)
default:
println("Associated value is nil") // Will print "Associated value is nil"
}
x = MyEnum.SomeCase(20)
x.someFunc()
switch x {
case .SomeCase(.Some(let a)):
println(a) // Will print 5
default:
println("Associated value is nil")
}

Swift enum with custom initializer loses rawValue initializer

I have tried to boil this issue down to its simplest form with the following.
Setup
Xcode Version 6.1.1 (6A2008a)
An enum defined in MyEnum.swift:
internal enum MyEnum: Int {
case Zero = 0, One, Two
}
extension MyEnum {
init?(string: String) {
switch string.lowercaseString {
case "zero": self = .Zero
case "one": self = .One
case "two": self = .Two
default: return nil
}
}
}
and code that initializes the enum in another file, MyClass.swift:
internal class MyClass {
let foo = MyEnum(rawValue: 0) // Error
let fooStr = MyEnum(string: "zero")
func testFunc() {
let bar = MyEnum(rawValue: 1) // Error
let barStr = MyEnum(string: "one")
}
}
Error
Xcode gives me the following error when attempting to initialize MyEnum with its raw-value initializer:
Cannot convert the expression's type '(rawValue: IntegerLiteralConvertible)' to type 'MyEnum?'
Notes
Per the Swift Language Guide:
If you define an enumeration with a raw-value type, the enumeration automatically receives an initializer that takes a value of the raw value’s type (as a parameter called rawValue) and returns either an enumeration member or nil.
The custom initializer for MyEnum was defined in an extension to test whether the enum's raw-value initializer was being removed because of the following case from the Language Guide. However, it achieves the same error result.
Note that if you define a custom initializer for a value type, you will no longer have access to the default initializer (or the memberwise initializer, if it is a structure) for that type. [...]
If you want your custom value type to be initializable with the default initializer and memberwise initializer, and also with your own custom initializers, write your custom initializers in an extension rather than as part of the value type’s original implementation.
Moving the enum definition to MyClass.swift resolves the error for bar but not for foo.
Removing the custom initializer resolves both errors.
One workaround is to include the following function in the enum definition and use it in place of the provided raw-value initializer. So it seems as if adding a custom initializer has a similar effect to marking the raw-value initializer private.
init?(raw: Int) {
self.init(rawValue: raw)
}
Explicitly declaring protocol conformance to RawRepresentable in MyClass.swift resolves the inline error for bar, but results in a linker error about duplicate symbols (because raw-value type enums implicitly conform to RawRepresentable).
extension MyEnum: RawRepresentable {}
Can anyone provide a little more insight into what's going on here? Why isn't the raw-value initializer accessible?
This bug is solved in Xcode 7 and Swift 2
extension TemplateSlotType {
init?(rawString: String) {
// Check if string contains 'carrousel'
if rawString.rangeOfString("carrousel") != nil {
self.init(rawValue:"carrousel")
} else {
self.init(rawValue:rawString)
}
}
}
In your case this would result in the following extension:
extension MyEnum {
init?(string: String) {
switch string.lowercaseString {
case "zero":
self.init(rawValue:0)
case "one":
self.init(rawValue:1)
case "two":
self.init(rawValue:2)
default:
return nil
}
}
}
You can even make the code simpler and useful without switch cases, this way you don't need to add more cases when you add a new type.
enum VehicleType: Int, CustomStringConvertible {
case car = 4
case moped = 2
case truck = 16
case unknown = -1
// MARK: - Helpers
public var description: String {
switch self {
case .car: return "Car"
case .truck: return "Truck"
case .moped: return "Moped"
case .unknown: return "unknown"
}
}
static let all: [VehicleType] = [car, moped, truck]
init?(rawDescription: String) {
guard let type = VehicleType.all.first(where: { description == rawDescription })
else { return nil }
self = type
}
}
Yeah this is an annoying issue. I'm currently working around it using a global-scope function that acts as a factory, i.e.
func enumFromString(string:String) -> MyEnum? {
switch string {
case "One" : MyEnum(rawValue:1)
case "Two" : MyEnum(rawValue:2)
case "Three" : MyEnum(rawValue:3)
default : return nil
}
}
This works for Swift 4 on Xcode 9.2 together with my EnumSequence:
enum Word: Int, EnumSequenceElement, CustomStringConvertible {
case apple, cat, fun
var description: String {
switch self {
case .apple:
return "Apple"
case .cat:
return "Cat"
case .fun:
return "Fun"
}
}
}
let Words: [String: Word] = [
"A": .apple,
"C": .cat,
"F": .fun
]
extension Word {
var letter: String? {
return Words.first(where: { (_, word) -> Bool in
word == self
})?.key
}
init?(_ letter: String) {
if let word = Words[letter] {
self = word
} else {
return nil
}
}
}
for word in EnumSequence<Word>() {
if let letter = word.letter, let lhs = Word(letter), let rhs = Word(letter), lhs == rhs {
print("\(letter) for \(word)")
}
}
Output
A for Apple
C for Cat
F for Fun
Add this to your code:
extension MyEnum {
init?(rawValue: Int) {
switch rawValue {
case 0: self = .Zero
case 1: self = .One
case 2: self = .Two
default: return nil
}
}
}