It sounds reasonable that the discriminator of an object variant should be kept
invariable to keep the object's data structure safe; I found, however, some
scenarios in which it can be done. One way was to use the compiler directive
--fieldChecks:off. I also attempted the same, but using the pragma
{.fieldChecks: off.} and it worked. In fact I just found about that pragma by
intuition, as it's not documented anywhere as far as I could research. I learnt
also that you can use the pragma {.fieldChecks: on.} to bring back checking
after the "unsafe" procedures are declared. I assume that "unsafe" procedures
are all those that may use the discriminator once it has been changed.
For example, in the code below,
type
MyKind = enum mk1, mk2
MyObj = object
case mk: MyKind:
of mk1: x, y, z: float
of mk2: u, v, w: float
proc myProc(mk: MyKind; i, j, k: float): MyObj =
case mk:
of mk1: return MyObj(mk: mk1, x: i, y: j, z: k)
of mk2: return MyObj(mk: mk2, u: i, v: j, w: k)
{.fieldChecks: off.} # disable field checks for object variants
proc change(mo: var MyObj; mkTo: MyKind) =
# this is an unsafe proc that changes the discriminator
# so the fieldChecks must me turned off to avoid runtime errors
mo.mk = mkTo
{.fieldChecks: on.} # turn field checks back on
{.fieldChecks: off.} # disable field checks again, this proc `$` seems to
fail at runtime otherwise
proc `$`(mo: MyObj): string =
# seemingly because of the previous proc, `$` also needs fieldChecks off
# this can be made public* as it is safe to use
result = "[mk: " & $mo.mk & "]:("
case mo.mk:
of mk1: result &= "x:" & $mo.x & ", y:" & $mo.y & ", z:" & $mo.z
of mk2: result &= "u:" & $mo.x & ", v:" & $mo.y & ", w:" & $mo.z
result &= ")"
{.fieldChecks: on.} # turn field checks back on
proc rotate(mo: var MyObj) =
# rotates the contents of mo regardless of what kind it is
# this proc seems not to require fieldChecks turned off
var
temp: float
orik: MyKind = mo.mk # this is the original kind
change(mo, mk1) # temporary kind change
# all kinds are treated as mk1, so less code is required
temp = mo.x
mo.x = mo.y
mo.y = mo.z
mo.z = temp
change(mo, orik) # kind back to the original kind
var
p, q: MyObj
p = myProc(mk1, 1, 2, 3) # p starts as kind "mk1"
echo "p = ", p # output: p = [mk: mk1](x:1.0, y:2.0, z:3.0)
change(p, mk2) # p is changed to kind "mk2"
echo "p = ", p # output: p = [mk: mk2](u:1.0, v:2.0, w:3.0)
rotate(p) # parameters of P are rotated
echo "p = ", p # output: p = [mk: mk2](u:1.0, v:2.0, w:3.0)
q = myProc(mk2, 4, 5, 6) # q starts as kind "mk2"
echo "q = ", q # output: q = [mk: mk2](u:4.0, v:5.0, w:6.0)
rotate(q) # parameters of q are rotated
echo "q = ", q # output: q = [mk: mk2](u:5.0, v:6.0, w:4.0)
Run