Skip to content

Einen verschachtelten Wert tief klonen snippet

Eine flache Kopie dupliziert den äußeren Container und teilt alles Verschachtelte — ein Feld eine Ebene tiefer mutieren, und beide Kopien ändern sich.

Eine flache Kopie dupliziert den äußeren Container und teilt alles Verschachtelte — ein Feld eine Ebene tiefer mutieren, und beide Kopien ändern sich. Die Sprachen spalten sich dreifach: Rust und Swift kopieren standardmäßig TIEF (Teilen wird explizit per Opt-in aktiviert), JavaScript und Kotlin geben flaches Spread/copy(), das tief aussieht, und Go/Zig kopieren Bytes flach, ohne jede stdlib-Tiefe. Der universelle Fallback — serialisieren und zurückparsen — funktioniert überall, verwirft aber still alles, was das Format nicht darstellen kann (Dates zu Strings, undefined, unexportierte Felder).

Runnable recipe · 13 languages
Files & Dataclonecopystructured-clonevalue-semanticsimmutability

Every language

13 languages, copy-ready. One at a time with syntax highlighting, or all inline.

JSJavaScript
// spread is SHALLOW — one level only. This is the real deep clone:
const copy = structuredClone(value); // Dates, Maps, Sets, cycles: all kept

The old JSON.parse(JSON.stringify(value)) trick loses Dates (→ strings), undefined, Map/Set, and dies on cycles. structuredClone (all runruns since 2022) handles them.

TSTypeScript
declare const state: { user: { name: string; tags: string[] } };

const copy: typeof state = structuredClone(state); // typed as T in, T out
copy.user.tags.push('x'); // original untouched

structuredClone's lib type is <T>(value: T): T — the clone keeps the compile-time shape. Functions cannot be cloned; the call throws on them.

GoGo
// no stdlib deep copy — the data-only struct roundtrip:
func deepClone[T any](v T) (T, error) {
	var out T
	b, err := json.Marshal(v)
	if err != nil {
		return out, err
	}
	err = json.Unmarshal(b, &out)
	return out, err
}

The JSON hop drops unexported fields and needs the struct exported + serializable. Graphs with cycles or func fields: write the copy by hand.

RsRust
#[derive(Clone)]
struct Config {
    name: String,
    nested: Vec<String>,
}

let copy = original.clone(); // DEEP: every owned value duplicated

The inversion: Rust clones deep by default — sharing must be explicit (Rc/Arc make .clone() bump a refcount instead). The question this recipe asks barely exists here.

PHPPHP
// nested ARRAYS copy deep on assignment (value semantics)…
$b = $a; // deep for nested arrays

// …but objects inside are shared references:
$clone = unserialize(serialize($object)); // the classic deep clone

clone $obj copies one level; nested objects stay shared. serialize/unserialize handles the graph but breaks closures and resources.

PyPython
import copy

deep = copy.deepcopy(nested)  # graphs, cycles handled

# all of these are SHALLOW for nested mutables:
# b = a · b = a[:] · b = list(a) · b = dict(a) · b = a.copy()

Slicing a list of lists copies the outer list; the inner lists are shared objects. deepcopy also memoizes, so shared sub-objects stay shared in the clone.

C++C++
// value semantics: the compiler-generated copy constructor deep-copies
// every member that itself has value semantics:
struct Config {
    std::string name;              // deep: a new string
    std::vector<std::string> tags; // deep: a new vector of new strings
};

auto copy = original; // DEEP by construction — no clone() call anywhere

The rule is deep-by-construction: std::string, std::vector, and every RAII type clone correctly inside the default copy constructor. A raw pointer member stays SHARED between copies — which is why raw owning pointers are banned in modern C++; hold members by value or by smart pointer.

C#C#
using System.Text.Json;

var copy = JsonSerializer.Deserialize<User>(
    JsonSerializer.Serialize(original));

Record with-expressions (original with { ... }) are shallow, like Kotlin's copy(). The JSON hop needs parameterless constructors or JsonSerializerOptions with custom factory.

JvJava
// Object.clone() is shallow and effectively deprecated.
// The modern Java answer: make it immutable — then shallow IS deep.
record Address(String city) {}
record User(String name, Address address) {}

User copy = new User(original.name(), original.address());

Immutable records end the question: no mutation is possible, so sharing nested values is safe and 'cloning' means copying the references you want to change.

SwSwift
// STRUCTS: assignment IS a deep copy — value semantics
var copy = original // nested structs and arrays fully independent

// CLASSES are reference types — a class tree needs explicit cloning

One language, both answers: struct copies deep (arrays/strings are copy-on-write underneath, so it is cheap), class instances share. Prefer structs; the compiler does the rest.

KtKotlin
data class User(val name: String, val tags: List<String>)

val copy = original.copy(name = "Ada") // SHALLOW: tags list is shared

THE Kotlin trap: data-class copy() is a shallow field-by-field copy. Nested mutables stay shared — combine with a deep clone of the fields you will mutate.

RbRuby
shallow = obj.dup   # one level

deep = Marshal.load(Marshal.dump(obj)) # the stdlib deep clone

Marshal roundtrip handles nested state but raises on procs, singletons, and IO objects. dup keeps frozen state; clone copies it.

ZigZig
// assignment copies the struct's bytes — pointers and slices stay shared.
// For plain-data structs, the JSON roundtrip is the honest stdlib deep copy:
const std = @import("std");

fn deepClone(comptime T: type, alloc: std.mem.Allocator, value: T) !T {
    var buf = std.ArrayList(u8).init(alloc);
    defer buf.deinit();
    try std.json.stringify(value, .{}, buf.writer());
    const parsed = try std.json.parseFromSlice(T, alloc, buf.items, .{});
    defer parsed.deinit(); // copies out below keep it valid
    return parsed.value;
}

parseFromSlice returns an arena-owned value — returning parsed.value by value copies it out (safe for data structs, not for pointers the arena owns). Slices inside the struct point INTO the arena: real clones need explicit allocation.