Swift 将与值关联的值类型进行适当的变异

Swift 将与值关联的值类型进行适当的变异,swift,enums,pass-by-reference,value-type,Swift,Enums,Pass By Reference,Value Type,在Swift中,是否可以变异与枚举关联的值(这是一种值类型)?如果是,如何进行 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

在Swift中,是否可以变异与枚举关联的值(这是一种值类型)?如果是,如何进行

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 
} 
注意:这似乎是可能的,至少对于
可选的
枚举
s?.a=2
变异
可选的


用例:

我正在编写一个关于
Codable
的小包装器,以便轻松地在字典和对象之间映射值。我有一个
MappingContext
,它是
encode
decode
。假设
KeyedEncodingContainer
在编码时有一个变异函数,我希望此包装的用户避免重新分配值,而只是直接变异关联的值

我打开了swift stdlib代码,该函数甚至没有改变结构,但标记为mutating。不知道为什么,但我现在可以没有问题地复制,而且重新分配是一个可以接受的解决方案,只是想确保我没有遗漏任何明显的内容

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


public enum CodingMode {
    case encode(EncodeContainer)
    case decode(DecodeContainer)
}
typealias EncodeContainer=KeyedEncodingContainer
typealias DecodeContainer=KeyedDecodeContainer
公共枚举编码模式{
案例编码(EncodeContainer)
案例解码(解码容器)
}

如果不覆盖枚举本身,则无法更改与枚举相关的值;如果将
S
声明为
class
,则这是一项非常简单的任务

无论如何,我做了一些实验,我在这里报告:

1) 基于:


2) 声明用于隐式覆盖枚举的变异
func

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) 如果
S

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)

你到底为什么需要它“到位”?对于值类型,执行读-修改-写操作与就地操作具有相同的语义。您可以在对本地副本进行变异后分配
e=.s
。在Swift中,您可以尝试使用
UnsafemeutablePointer
moveAssign(from:count:)
。但是,您仍然需要值的地址,并且需要构造一个新值(或子组件)。从反汇编代码来看,与读-修改-写方法相比,似乎不值得这么做。如果你试图解决一些奇特的问题,比如在枚举中有一个pthRead互斥体,你需要手动初始化和销毁,这可能希望你回到C++。这将用一个新的
e
值覆盖
e
,方法与
e=e.s(s(a:2))
相同。是的,它会。但是我改变了值,而不是指针。在我的控制台上:
0x00007fbe355083c0
s(TestMutate.ViewController.s(a:2))
0x00007fbe355083c0
你是如何得到那个地址的?
打印(\($0)”
我看不出
带不可更改指针(to:&e,{$0.pointee=e.s(a:2))
e=e.s(a:2))
。两者都创建一个新的
E
值并将其分配给
E
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)