I found myself needing a deep copy of a ref object, and reached for the aptly named deepCopy proc in System.
But then I got the error: "for --gc:arc|orc 'deepcopy' support has to be enabled with --deepcopy:on"
I suppose I'll turn it on, but it seems like there must be a performance trade-off? (If not, should this flag be the default?)
I do see past discussion about deprecating deepCopy in favor "move" or "send" (names I would never guess). And I'm getting the distinct impression that using deepCopy is bad practice somehow.
But even outside the context of multi-threading, would it not sometimes be useful to create a new object by (deep)copying an old one and tweaking it? It's not maximally efficient, but saves a lot of boilerplate vs constructing a new object using each of the old object's properties one by one, especially for big objects.
But I want to improve, so if there's some reason for avoiding it, please let me know.
Then you do this:
proc myDeepCopy[T](src: ref T): ref T =
new(result)
result[] = src[]
proc myDeepCopy[T](dst, src: ref T) =
dst[] = src[]
Correct, that won't copy children refs. Which is why deepcopy is useful.
But I've also found myself wondering why deepcopy needs a separate compiler option and what are the tradeoffs involved. I hope araq or someone else familiar with ARC internals will be able to shed some light.
From Nim 1.4 release blog post:
Known incompatibilities and gotchas Deepcopy If you use system.deepCopy in your code, you need to enable it via --deepCopy:on on the command line. This is a band-aid and a better solution for this will arrive in the future.
The reason for this opt-in switch is that ARC and ORC do not use the old runtime type information (RTTI) that system.deepCopy is built upon. We’ve built ARC and ORC with embedded devices in mind where the overhead of the old RTTI has been reported to be unacceptable.
Oh, that explains it.
Thanks
How about
proc myDeepCopy[T](src: ref T): ref T =
new(result)
`=copy`(result[], src[])
proc myDeepCopy[T](dst, src: ref T) =
`=copy`(dst[], src[])
No, you have to redefine it in a way that recurses.
proc deepCopy[T: ptr | ref](dst: var T, src: T) =
deepCopy(dst[], src[])
proc deepCopy[T: Ordinal | string | cstring | proc | set](dst: var T, src: T) =
dst = src
proc deepCopy[I, T](dst: var array[I, T], src: array[I, T]) =
for i in low(src)..high(src):
deepCopy(dst[i], src[i])
proc deepCopy[T](dst: var seq[T], src: seq[T]) =
dst = newSeq[T](src.len)
for i in 0..<src.len:
deepCopy(dst[i], src[i])
proc deepCopy[T: object | tuple](dst: var T, src: T) =
# `fields` with 2 parameters doesn't work for object variants but we can easily write a version of it that does, pretend we did so here
for a, b in fields(dst, src):
deepCopy(a, b)
# etc etc
Both references could be nil.
proc deepCopy[T](dst: var ref T; src: ref T) =
if unlikely(src == nil):
dst = nil
else:
if dst == nil:
dst.new()
deepCopy(dst[], src[])