[]T []int
[]string
[]User
[]byte
[]map[string]any nums := []int{1, 2, 3} nums = append(nums, 4) [1 2 3 4] s1 := []int{1, 2, 3}
s2 := s1[:2]
s2[0] = 100
fmt.Println(s1) // [100 2 3]
var a [3]int
var b [4]int var a [3]int
var b [4]int
// a = b // compile error var s []int func changeArray(a [3]int) {
a[0] = 100
}
func main() {
arr := [3]int{1, 2, 3}
changeArray(arr)
fmt.Println(arr) // [1 2 3]
} func changeSlice(s []int) {
s[0] = 100
}
func main() {
nums := []int{1, 2, 3}
changeSlice(nums)
fmt.Println(nums) // [100 2 3]
}
type sliceHeader struct {
ptr *T
len int
cap int
} s := make([]int, 3, 6)
fmt.Println(len(s)) // 3
fmt.Println(cap(s)) // 6
fmt.Println(s) // [0 0 0] len = 3
cap = 6
[0 0 0 _ _ _]
^ доступная часть s[1] = 10
fmt.Println(s) // [0 10 0] s[4] = 100 // panic: index out of range words := []string{"go", "map", "go", "slice", "map", "go"}
counts := make(map[string]int)
for _, word := range words {
counts[word]++
}
fmt.Println(counts) // map[go:3 map:2 slice:1] len отвечает за доступные элементы. cap показывает запас базового массива. Индексировать можно только в пределах len, а не cap.
nums := []int{1, 2, 3}
words := []string{"go", "slice", "append"} s := make([]int, 5)
fmt.Println(s) // [0 0 0 0 0]
fmt.Println(len(s)) // 5
fmt.Println(cap(s)) // 5 s := make([]int, 0, 100)
fmt.Println(len(s)) // 0
fmt.Println(cap(s)) // 100 var s []int
s = append(s, 1) var s []int
fmt.Println(s == nil) // true
fmt.Println(len(s)) // 0
s = append(s, 1)
fmt.Println(s) // [1]
s := make([]int, 3, 5)
fmt.Println(len(s)) // 3
fmt.Println(cap(s)) // 5 s := []int{0, 1, 2, 3, 4, 5}
part := s[2:4]
fmt.Println(part) // [2 3]
fmt.Println(len(part)) // 2
fmt.Println(cap(part)) // 4
s := []int{1, 2, 3}
s = append(s, 4)
fmt.Println(s) // [1 2 3 4] s = append(s, 10) append(s, 10) // compile error: value of append is not used s := make([]int, 0, 3)
s = append(s, 1)
s = append(s, 2)
fmt.Println(len(s)) // 2
fmt.Println(cap(s)) // 3 s := make([]int, 0, 2)
s = append(s, 1, 2)
s = append(s, 3)
fmt.Println(len(s)) // 3
fmt.Println(cap(s)) // обычно больше 2
s1 := []int{1, 2, 3}
s2 := s1[1:2]
s3 := append(s2, 10)
fmt.Println(s1) // [1 2 10]
fmt.Println(s2) // [2]
fmt.Println(s3) // [2 10] func addSomething(s []int) {
_ = append(s, 10)
}
func main() {
nums := []int{1, 2, 3}
addSomething(nums[:2])
fmt.Println(nums) // [1 2 10]
} s[low:high:max] func addSomething(s []int) {
_ = append(s, 10)
}
func main() {
nums := []int{1, 2, 3}
addSomething(nums[:2:2])
fmt.Println(nums) // [1 2 3]
} Полное выражение слайса защищает от побочного изменения через append, но не решает проблему удержания большого базового массива в памяти. Для этого нужна копия.
copy(dst, src) src := []int{1, 2, 3}
var dst []int
copy(dst, src)
fmt.Println(dst) // [] src := []int{1, 2, 3}
dst := make([]int, len(src))
copy(dst, src)
fmt.Println(dst) // [1 2 3] src := []int{1, 2, 3, 4}
dst := make([]int, 2)
n := copy(dst, src)
fmt.Println(dst) // [1 2]
fmt.Println(n) // 2 dst := append([]int(nil), src...)
var s []string s := []string{} s := make([]string, 0) var a []string
b := []string{}
c := make([]string, 0)
fmt.Println(a == nil) // true
fmt.Println(b == nil) // false
fmt.Println(c == nil) // false
fmt.Println(len(a)) // 0
fmt.Println(len(b)) // 0
fmt.Println(len(c)) // 0 var s []int
fmt.Println(len(s)) // 0
for _, v := range s {
fmt.Println(v) // не выполнится
}
s = append(s, 1)
fmt.Println(s) // [1] type Response struct {
Items []string `json:"items"`
}
var nilSlice []string
emptySlice := []string{}
b1, _ := json.Marshal(Response{Items: nilSlice})
b2, _ := json.Marshal(Response{Items: emptySlice})
fmt.Println(string(b1)) // {"items":null}
fmt.Println(string(b2)) // {"items":[]} Практика: если ты просто собираешь результат в коде, var s []T нормален. Если отдаёшь JSON и хочешь именно пустой массив, инициализируй []T{} или make([]T, 0).
if len(s) == 0 {
// пусто
} func handle(items []Item) {
if items != nil {
process(items)
}
} items := []Item{}
handle(items) if len(items) > 0 {
process(items)
} if len(s) == 0 {
// пусто
}
s := []int{0, 1, 2, 3, 4}
part := s[1:4]
fmt.Println(part) // [1 2 3] s[low:high] s := []string{"a", "b", "c", "d"}
fmt.Println(s[:2]) // [a b]
fmt.Println(s[2:]) // [c d]
fmt.Println(s[:]) // [a b c d] s := []int{1, 2, 3}
part := s[1:]
part[0] = 100
fmt.Println(s) // [1 100 3]
fmt.Println(part) // [100 3] part := s[1:3:3] cap = max - low
func removeAt(s []int, i int) []int {
return append(s[:i], s[i+1:]...)
} nums := []int{10, 20, 30, 40}
nums = removeAt(nums, 1)
fmt.Println(nums) // [10 30 40] func removeAtUnordered(s []int, i int) []int {
s[i] = s[len(s)-1]
return s[:len(s)-1]
} nums := []int{10, 20, 30, 40}
nums = removeAtUnordered(nums, 1)
fmt.Println(nums) // [10 40 30] func removePtr(s []*User, i int) []*User {
copy(s[i:], s[i+1:])
s[len(s)-1] = nil
return s[:len(s)-1]
}
Для слайсов с указателями удаление через append(s[:i], s[i+1:]...) может оставить ссылку в хвосте базового массива. В долгоживущих структурах это превращается в неприятные утечки.
func containsInt(s []int, target int) bool {
for _, v := range s {
if v == target {
return true
}
}
return false
} func containsString(s []string, target string) bool {
for _, v := range s {
if v == target {
return true
}
}
return false
} import "slices"
ok := slices.Contains([]int{1, 2, 3}, 2)
fmt.Println(ok) // true ids := []int{10, 20, 30}
set := make(map[int]struct{}, len(ids))
for _, id := range ids {
set[id] = struct{}{}
}
_, ok := set[20] nums := []int{3, 1, 2}
sort.Ints(nums)
fmt.Println(nums) // [1 2 3] words := []string{"go", "append", "slice"}
sort.Strings(words)
fmt.Println(words) // [append go slice] type User struct {
Name string
Age int
}
users := []User{
{Name: "Mark", Age: 24},
{Name: "Niyaz", Age: 30},
}
sort.Slice(users, func(i, j int) bool {
return users[i].Age < users[j].Age
}) slices.Sort(nums) slices.SortFunc(users, func(a, b User) int {
return cmp.Compare(a.Age, b.Age)
}) words := []string{"go", "slice", "map"} b := []byte{'g', 'o'}
s := string(b)
fmt.Println(s) // go s := "golang"
b := []byte(s)
fmt.Println(b) s := "go"
b := []byte(s)
b[0] = 'G'
fmt.Println(string(b)) // Go
fmt.Println(s) // go s := "привет"
runes := []rune(s)
fmt.Println(len(s)) // байты
fmt.Println(len(runes)) // символы
m := map[string]int{
"go": 3,
"slice": 2,
"map": 1,
}
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
fmt.Println(keys) values := make([]int, 0, len(m))
for _, v := range m {
values = append(values, v)
} type Pair struct {
Key string
Value int
}
pairs := make([]Pair, 0, len(m))
for k, v := range m {
pairs = append(pairs, Pair{Key: k, Value: v})
} src := []int{1, 2, 3}
dst := make([]int, len(src))
copy(dst, src) src := map[string]int{"a": 1}
dst := make(map[string]int, len(src))
for k, v := range src {
dst[k] = v
} src := map[string][]int{
"a": {1, 2, 3},
}
dst := make(map[string][]int, len(src))
for k, v := range src {
copied := make([]int, len(v))
copy(copied, v)
dst[k] = copied
} Практика: в Go лучше не пытаться сделать deep copy на все случаи жизни. Обычно безопаснее явно копировать конкретные структуры данных, которые используются в твоём коде.
func convert(foos []Foo) []Bar {
bars := make([]Bar, 0)
for _, foo := range foos {
bars = append(bars, fooToBar(foo))
}
return bars
} func convert(foos []Foo) []Bar {
bars := make([]Bar, 0, len(foos))
for _, foo := range foos {
bars = append(bars, fooToBar(foo))
}
return bars
} func convert(foos []Foo) []Bar {
bars := make([]Bar, len(foos))
for i, foo := range foos {
bars[i] = fooToBar(foo)
}
return bars
} func collectKeys(items []Item) []string {
keys := make([]string, 0, len(items)*2)
for _, item := range items {
keys = append(keys, item.StartKey, item.EndKey)
}
return keys
} func collectKeys(items []Item) []string {
keys := make([]string, len(items)*2)
for i, item := range items {
keys[i*2] = item.StartKey
keys[i*2+1] = item.EndKey
}
return keys
}
func getMessageType(msg []byte) []byte {
return msg[:5]
} func getMessageType(msg []byte) []byte {
msgType := make([]byte, 5)
copy(msgType, msg[:5])
return msgType
} func getMessageType(msg []byte) []byte {
return append([]byte(nil), msg[:5]...)
} return msg[:5:5] type Foo struct {
Data []byte
} func keepFirstTwo(foos []Foo) []Foo {
return foos[:2]
} func keepFirstTwo(foos []Foo) []Foo {
res := make([]Foo, 2)
copy(res, foos[:2])
return res
} func keepFirstTwo(foos []Foo) []Foo {
for i := 2; i < len(foos); i++ {
foos[i].Data = nil
}
return foos[:2]
} func change(s []int) {
s[0] = 100
}
func main() {
nums := []int{1, 2, 3}
change(nums)
fmt.Println(nums) // [100 2 3]
} func add(s []int) {
s = append(s, 4)
}
func main() {
nums := []int{1, 2, 3}
add(nums)
fmt.Println(nums) // [1 2 3]
} func add(s []int) []int {
return append(s, 4)
}
func main() {
nums := []int{1, 2, 3}
nums = add(nums)
fmt.Println(nums) // [1 2 3 4]
} func filterPositive(nums []int) []int {
res := make([]int, 0, len(nums))
for _, n := range nums {
if n > 0 {
res = append(res, n)
}
}
return res
} func filterPositiveInPlace(nums []int) []int {
res := nums[:0]
for _, n := range nums {
if n > 0 {
res = append(res, n)
}
}
return res
} Практика: in-place фильтрация хороша для внутренних буферов и hot path. Для публичных API и кода, где важна предсказуемость, лучше вернуть новый слайс.
var nums []int
for i := 0; i < 10; i++ {
go func(i int) {
nums = append(nums, i)
}(i)
} var (
mu sync.Mutex
nums []int
)
for i := 0; i < 10; i++ {
go func(i int) {
mu.Lock()
defer mu.Unlock()
nums = append(nums, i)
}(i)
} ch := make(chan int)
for i := 0; i < 10; i++ {
go func(i int) {
ch <- i
}(i)
} s := make([]int, 3, 10)
s[5] = 1 // panic append(s, 1) // нельзя s = append(s, 1) part := s[:2]
part[0] = 100 var dst []int
copy(dst, src) // скопирует 0 элементов dst := make([]int, len(src))
copy(dst, src) if s != nil {
// не значит, что есть элементы
} if len(s) > 0 {
// есть элементы
} return bigData[:5]
type Response struct {
Items []Item `json:"items"`
} ids := make([]int64, 0, len(rows)) batch := make([]Event, 0, 1000) tasks := []Task{} stack := []Node{root} runes := []rune(text)
s := []int{1, 2, 3} s := make([]int, 0, 100) s = append(s, 10) s = append(s, other...) dst := make([]int, len(src))
copy(dst, src) s = append(s[:i], s[i+1:]...) s[i] = s[len(s)-1]
s = s[:len(s)-1] len(s) == 0 slices.Contains(s, x) slices.Sort(s) str := string(bytes) bytes := []byte(str)