Programming Languages - Assignment Semantics
3 kinds of assignment semantics:
- share semantics
- copy semantics
- move semantics
E.g. for this pseudocode
v1 = {1, 2, 3}
v2 = v1
- Share semantics (e.g. Java): the header of v1 is copied to the header of v2; v1 and v2 both refer to the same heap buffer.
- Copy semantics (e.g. C++): another heap buffer is allocated, content of v1 is copied to v2; v1 and v2 refer to distinct objects that happen to be the same initially.
- Move semantics (e.g. Rust and C++
std::move()): the header of v1 is copied to the header of v2; v1 cannot be used after the assignment.
By language:
| Share | Copy | Move | |
|---|---|---|---|
| Java | Yes | ||
| C++ | Default | std::move |
|
| Rust | .clone() |
Default | |
| Go | Default |
Rust
Default move (however for primitive numbers, static strings, and references, Rust use copy semantics); to copy: let v2 = v1.clone().
copyable vs clonable:
- copyable (implicitly cloneable): objects that cannot own anything and are easy and cheap to copy. E.g. numbers.
- cloneable but not copyable: objects that may own some heap objects and not cheap to be copied. E.g. collections.
- not cloneable: objects that own external resources like a file handle or a GUI window handle.
C++
Default copy; to move: std::move(v1).
Go
Copy semantics. Does not support move semantics.
v1 := [3]int{1, 2, 3}
v2 := v1
v1[0] = 1000
fmt.Println(v1)
// [1000 2 3]
fmt.Println(v2)
// [1 2 3]
Note if we pass a slice to a func, slice header is passed by value, so the function receives a copy of the header, but the header points to the same underlying array.
func modify(s []int) {
s[0] = 100
}
func main() {
// s is a slice
s := []int{1, 2, 3}
modify(s)
fmt.Println(s)
// [100 2 3]
}
If we pass the array, it will be a copy inside the func:
func modify(a [3]int) {
a[0] = 100
}
func main() {
// a is an array
a := [3]int{1, 2, 3}
modify(a)
fmt.Println(a)
// [1 2 3]
}
Deallocation
- In C / C++, heap objects used to be deallocated explicitly by
free,delete(not any more if you use smart pointers). - In Java / C#, heap objects are not immediately deallocated, they are cleaned up by garbage collector.
- In Rust, or modern C++, heap objects are deallocated when the owner is unreachable. (Rust:
Box<T>; C++:std::unique_ptr<T>)
| language | deterministic? | explicit? |
|---|---|---|
C / C++ free, delete |
deterministic | explicit |
| Java / C# | nondeterministic | implicit |
| Rust / C++ smart pointer | deterministic | implicit |
Recommended ReadingSponsored
Concepts
Overview
Which Programming Languages To Learn
Compile Time and Runtime
Standard Libraries
Build Tools
Memory Management
Zero Cost Abstractions
Design Patterns
Constructors
Compatibility
Closures
Functional Programming
Generics
String Interning
Coding Conventions
Main
Ranges
Lambda
Random
IO
Operators
Built-in
Constants
Functions
Generics
Print
Pointers
Annotations
Quotes
Destructuring
Generators
Imports
Iterators
Testing
Visibility
Assignment Semantics
Slices
SSA
Versus
Concurrency
Control Flow
Data Structures

