Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mutate a value-type associated value in-place

Is it possible in Swift to mutate an enum associated value, which is a value type? If yes, How?

struct S { 
    var a = 1 
}

enum E { 
    case s(S) 
}

var e = E.s(S())
// the goal is to make e hold s with a = 2
// overwriting e is not allowed

if case var .s(s) = e { 
   // s is a copy at this point
   // I want to mutate e in-place
   s.a = 2 
} 

NOTE: it seems to be possible, at least for the Optional enum s?.a = 2 mutates Optional<S>.


Use case:

I am writing a tiny wrapper around Codable, to easily map values between dictionaries and objects. I have a single MappingContext, which is either encode or decode. Given that KeyedEncodingContainer has a mutating function while encoding, and I would like the user of this wrapper to avoid reassigning the value, and just directly mutate the associated value.

I opened swift stdlib code, and the function doesn't even mutate the struct, yet is marked mutating. Not sure why, but I can have copies without issues now, but also reassigning is an acceptable solution, just wanted to make sure I am not missing anything obvious.

typealias EncodeContainer = KeyedEncodingContainer<JsonCodingKey>
typealias DecodeContainer = KeyedDecodingContainer<JsonCodingKey>


public enum CodingMode {
    case encode(EncodeContainer)
    case decode(DecodeContainer)
}
like image 577
Mazyod Avatar asked Dec 20 '25 04:12

Mazyod


1 Answers

It's not possible to change an enum associated value, without overwriting the enum itself; if instead S is declared as class, it's a quite trivial task.

Anyway I did some experiments that I report here:

1) based on withUnsafeMutablePointer:

var e = E.s(S(a: 1))
withUnsafeMutablePointer(to: &e, {
   $0.pointee = E.s(S(a: 2))
})
print(e)

2) declaring a mutating func for overwriting implicitly the enum

struct S {
   var a = 1
}

enum E {
   case s(S)
   mutating func setS(val:S) { self = E.s(val) }
}

var e = E.s(S(a: 1))
e.setS(val: S(a: 2))
print(2)

3) if S is a class:

class S:CustomDebugStringConvertible {
   var a = 1
   var debugDescription: String { return "\(a)" }
}

enum E {
   case s(S)
}

let e = E.s(S())
if case let .s(s) = e {
   s.a = 2
}
print(e)
like image 168
Andrea Mugnaini Avatar answered Dec 21 '25 20:12

Andrea Mugnaini



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!