Skip to content

Commit 88b9a25

Browse files
committed
feat: Implement recursivecopy for structs
Only supports mutable structs, and has to have extra logic to dispatch on basic types...
1 parent 5493a98 commit 88b9a25

File tree

1 file changed

+55
-5
lines changed

1 file changed

+55
-5
lines changed

src/utils.jl

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,29 @@ unrolled_foreach!(f, ::Tuple{}) = nothing
33

44
"""
55
```julia
6-
recursivecopy(a::Union{AbstractArray{T, N}, AbstractVectorOfArray{T, N}})
6+
recursivecopy(a)
77
```
88
99
A recursive `copy` function. Acts like a `deepcopy` on arrays of arrays, but
10-
like `copy` on arrays of scalars.
11-
"""
12-
function recursivecopy(a)
13-
deepcopy(a)
10+
like `copy` on arrays of scalars. For struct types, recursively copies each
11+
field, creating new instances while preserving the struct type.
12+
13+
## Examples
14+
15+
```julia
16+
# Basic array copying
17+
arr = [[1, 2], [3, 4]]
18+
copied = recursivecopy(arr) # New arrays at each level
19+
20+
# Struct copying
21+
struct MyStruct
22+
data::Vector{Float64}
23+
metadata::String
1424
end
25+
original = MyStruct([1.0, 2.0], "test")
26+
copied = recursivecopy(original) # New struct with new vector
27+
```
28+
"""
1529
function recursivecopy(a::Union{StaticArraysCore.SVector, StaticArraysCore.SMatrix,
1630
StaticArraysCore.SArray, Number})
1731
copy(a)
@@ -35,6 +49,42 @@ function recursivecopy(a::AbstractVectorOfArray)
3549
return b
3650
end
3751

52+
function _is_basic_julia_type(T)
53+
# Check if this is a built-in Julia type that we should not handle as a user struct
54+
# We check the module to identify Core/Base types vs user-defined types
55+
mod = Base.parentmodule(T)
56+
return T <: AbstractString || T <: Number || T <: Symbol || T <: Tuple ||
57+
T <: UnitRange || T <: StepRange || T <: Regex ||
58+
T === Nothing || T === Missing ||
59+
mod === Core || mod === Base
60+
end
61+
62+
function recursivecopy(s::T) where {T}
63+
# Only handle user-defined immutable structs. Many basic Julia types (String, Symbol,
64+
# Tuple, etc.) are technically structs but should use copy() or return as-is.
65+
if Base.isstructtype(T) && !_is_basic_julia_type(T)
66+
if Base.ismutabletype(T)
67+
error("recursivecopy for mutable structs is not currently implemented. Use deepcopy instead.")
68+
else
69+
# Handle immutable structs only
70+
field_values = ntuple(fieldcount(T)) do i
71+
field_value = getfield(s, i)
72+
recursivecopy(field_value)
73+
end
74+
return T(field_values...)
75+
end
76+
elseif _is_basic_julia_type(T)
77+
# For basic Julia types, use copy if available, otherwise return as-is (for immutable types)
78+
if hasmethod(copy, Tuple{T})
79+
return copy(s)
80+
else
81+
return s # Immutable basic types like Symbol, Nothing, Missing don't need copying
82+
end
83+
else
84+
deepcopy(s)
85+
end
86+
end
87+
3888
"""
3989
```julia
4090
recursivecopy!(b::AbstractArray{T, N}, a::AbstractArray{T, N})

0 commit comments

Comments
 (0)