Some languages let you declare a type whose instances are copied whenever they are assigned or passed, while others give every user-defined type reference semantics. Compare the two language models and say what each one costs the programmer.
answer
- assignment copies object vs copies handle
- struct vs class: C# and Swift let the author choose
- Go: memberwise copy, no copy hook, copied mutex
- C++ only: object slicing into a base-typed variable
- value semantics != immutability
basics
~20 sValue semantics: assignment copies the object, so two names never alias one state. Reference semantics: assignment copies a handle. C#, Swift, Go and C++ let the type's author choose; in Java, Python and JavaScript every object is a reference.
solid answer
~50 sThe choice is made by the language, and only some languages delegate it to the type author. - **C#** decides at declaration: `struct` copies on assignment, `class` does not. The cost is that a struct used as `object` or through an interface is boxed, so the copy discipline quietly becomes a heap allocation. - **Swift** makes `struct` the default for new types and requires `mutating` on methods that write `self`, so accidental shared-state mutation is a compile error rather than a convention. - **Go** copies structs memberwise with no user hook at all — no copy constructor exists, which is why a copied `sync.Mutex` silently becomes two locks and `go vet` has a check for it. - **C++** is value-first with copy and move constructors, and pays with object slicing: assigning a derived object into a base-typed variable truncates it. - **Java, Python, JavaScript** offer no value types for objects; "value object" is a discipline. Java's Valhalla value classes are still a preview.
code
go · 6 linestype P struct{ X int }
a := P{X: 1}
b := a // full memberwise copy
b.X = 99
// a.X is still 1go deeper
Be able to say what changes when assignment copies the object versus copies a handle, and give one language of each kind.
Name which languages let the type author choose (C#, Swift, Go, C++) and which do not, and separate value semantics from immutability.
Discuss the costs: copy expense and in/const& mitigations, boxing when a C# struct is used through an interface, copied locks and handles, and C++ slicing.
Frame it as who owns the aliasing decision across a codebase, and when to impose the value-object discipline by convention because the platform will not enforce copying.
## The two models When you write `b = a`, a language must answer one question: does `b` now name the *same* object as `a`, or its own copy? Reference semantics gives `b` a second handle onto one piece of state, so a later mutation through either name is visible through both — the variables are **aliases**. Value semantics gives `b` its own state, so the two evolve independently — the variables hold **copies**. Nothing about this is a matter of syntax; it changes what a whole codebase must reason about, because aliasing is the precondition for almost every "who changed my object?" bug. ## Where the choice lives Languages disagree about who decides. **The type's author decides.** C# splits the world at the declaration keyword: `struct` types are copied on assignment, on parameter passing and on return; `class` types are handles. Swift does the same with `struct` versus `class` and, unusually, pushes the standard library and the community toward `struct` as the default. Go makes every struct a value: assignment copies each field. C++ is value-oriented from the ground up — a variable of class type *is* the object, and copying is governed by the copy constructor and copy assignment operator (and, since C++11, their move counterparts). **The language decides, and the answer is always "reference".** In Java, Python, JavaScript, Ruby and Kotlin (on the JVM), every user-defined object lives behind a handle. Argument passing copies the handle, never the object — which is why "is it pass by value or by reference?" is answered by saying the *handle* is passed by value. There is no keyword that changes this. Java's Project Valhalla aims to add value classes, but they remain a preview feature, so today the answer stands. ## What value semantics costs Copies are not free. A large struct passed by value is memcpy'd at every call; that is why C# offers `in` parameters and `ref readonly`, why Swift's standard library implements copy-on-write so an `Array` behaves as a value while sharing storage until the first write, and why C++ programmers reach for `const&`. More subtly, copying interacts badly with anything whose identity matters. A mutex, a file handle, an atomic counter, or an object that registered itself somewhere all break when duplicated. Go's copied `sync.Mutex` is the classic case: the copy protects nothing that the original protects. Because Go has no copy hook, the language cannot make the copy fail — it can only be linted. C++ pays a cost the reference-only languages cannot even express: **object slicing**. Because a base-typed variable is storage sized for the base, assigning a derived object into it copies only the base part and discards the rest, including the dynamic type. In Java or Python, assigning a subclass instance to a base-typed variable is just another handle to the same object. ## What reference semantics costs Every assignment is a potential alias, so any object you hand out can be mutated behind your back and any object you accept can change under you. The response is a *discipline* rather than a language feature: make the type immutable, define equality over its fields, expose no identity-revealing behaviour, and return a fresh instance from any operation that would have mutated. That discipline is the value-object recipe, and it exists precisely because the language will not enforce copying for you. ## Value semantics is not the same as immutability These are two axes and all four combinations exist. A C# mutable `struct` has value semantics without immutability (and is widely considered a trap, because mutating a copy you did not realise you had is invisible). A Java `String` is immutable with reference semantics — aliasing is fine precisely because nobody can mutate it. Swift's `struct` plus `let` gives you both. Conflating them is the most common interview error on this topic. ## Copies versus aliases: the practical checklist Before trusting a type to behave as a value, ask three questions. Does assignment copy, or does the language only copy the handle? If it copies, does the copy reach the whole field graph or stop at the first indirection? And does anything about the object depend on there being exactly one of it — a lock, a handle, a registration, a cache key? A type that answers "copy, deeply, and nothing depends on uniqueness" is a genuine value; anything else is a reference type wearing a value's clothes.
- If a language gives you value types, where does it stop giving you value semantics?At the first indirection. The generated copy is memberwise, so a Go struct with a slice field copies the slice header and both structs share one backing array, and a Swift struct holding a class reference shares that instance. Value semantics is a property of the entire reachable field graph, not of the outer declaration keyword.
- Why does Swift require a `mutating` keyword on struct methods when Java needs nothing similar?Because a Swift struct method may be invoked on a copy or on a `let` binding, the compiler must know whether the method writes `self`; marking it `mutating` lets the compiler reject the call on an immutable binding. Java has no value types, so a method always mutates the one shared object and there is nothing to distinguish.
- Is a Java `String` a value object even though it has reference semantics?It behaves as one at the design level: it is immutable, compares by content, and no operation reveals which instance you hold beyond identity comparison. That is the value-object recipe applied by discipline. The language still copies only the handle, which is safe precisely because nobody can mutate the target.
A reference is a shared spreadsheet link: everyone edits the one document. A value is emailing the file as an attachment: your edits are yours alone — and the file itself gets copied every time, whether or not that was cheap.
saying these in an interview costs you the question
- Saying Java passes objects by reference, or that records give Java value types in the language-model sense
- Claiming value semantics implies a deep copy — every mainstream implementation copies memberwise
- Treating immutability and value semantics as the same property
- Assuming copying a struct is always the cheap option, ignoring large structs, boxing in C#, and copied locks or handles