> [!note]
> [[Go]] popularized it as a de-facto dynamic array implementation, but this is applicable to other languages and libraries.
# [[Go]]
![[Slice.svg]]
> [!warning]
> [[2025-08-21]] thinking about this; I think go's implementation is an #antipattern . slice is a useful data type, even without [[Rust]] like ownership (as [[Zig]] uses them). However, the fact that you can `append()` to them is a terrible conflation of features.
>
> Perhaps they tried to reduce the number of (parameterized) types in the language, as in the initial version only the [[Channels and Goroutines|Channels]], maps, and slices were polymorphic data types. If I were to design a language, but still keep simplicity, I would've...
> - allowed slices to exist, but they only function as bounded pointers - i.e. data type of the form `(&T, len)`
> - have a separate data type called `ArrayList` that is the data type of the form `(&T, len, capacity)`.
>
> The problem with the `append` is that it only makes sense you have the ownership over the data; 2+ separate code location cannot do `apped(slice, x)` safely, so they're forced to make defensive copies _anyway_.
>
> the slices do allow to implement naive queues semi-efficiently, but I do wonder if this is more efficient than a circular buffers implementation.
Go popularized slices as they are the default type for dynamic arrays.
Slices are quite strange as they behave differently than the usual `std::vector<T>` or `java.util.ArrayList<T>`. Conceptually, slice is a pointer to a range of an array. However, the `append()` built-in makes a slice like a vector. This results in very unusual syntax of `s = append(s, elem)` to perform an operation that would appear `vec.add(elem)` in many different languages.
This ironically promotes immutable uses of `[]T`. The underlying elements can be modified, but `[]T` cannot be appended to modify the original slice. `*[]T` - pointer to slices - do allow this, but this is quite clunky to use and syntactically discouraged.
Without strict notion of [[Rust]]-like ownership, it is very easy to introduce accidental data sharing via slices. This problem is present in most languages with different mitigations; C++ has references and `const`s to control mutability; Java heavily promotes immutability via `final`. However, Go doesn't have as big of immutability culture as C++.
The same applies to any slice-like data structure in other languages. Rust formalizes this, but other languages provide them with sharp edges:
- [[Java]]'s `substring` behavior - [link](https://www.javaadvent.com/2012/12/changes-to-string-substring-in-java-7.html). Previously, Java's substring shared the in-memory representation. However, starting from Java 7, this "optimization" was eliminated.
- Another java example of [List.subList](https://docs.oracle.com/javase/8/docs/api/java/util/List.html#subList-int-int-). This method is explicit that the sublist is backed by the larger list. However, some semantic are ill-defined.
---
> [!thought]
> If you squint really hard, `append` looks like Lisp's `cons` and provides a very minimal persistent data structure like semantics.
# [[Zig]]
`[]T` is a built-in type, that acts as a bounded view of an underlying array.