slices.Delete removes one contiguous range from a slice and shifts the remaining suffix toward the front. The operation modifies the supplied backing storage, so its returned slice header is part of the result rather than an optional convenience.

Its signature accepts any slice element type:

func Delete[S ~[]E, E any](s S, i, j int) S

The half-open interval s[i:j] follows ordinary Go slicing rules. Elements at indices from i through j-1 are removed, while elements before i retain their positions.

Deletion shifts the surviving suffix

A deletion in the middle leaves no gap in the returned slice. Elements after the removed range move left to occupy the vacated positions.

package main

import (
    "fmt"
    "slices"
)

func main() {
    values := []string{"a", "b", "c", "d", "e"}
    values = slices.Delete(values, 1, 4)

    fmt.Println(values) // [a e]
}

The range [1:4] contains b, c, and d. The surviving e moves into the position directly after a, and the returned length becomes two.

Deleting an empty range is valid when the corresponding slice expression is valid:

values := []int{10, 20, 30}
values = slices.Delete(values, 1, 1)

fmt.Println(values) // [10 20 30]

No elements are removed in that case.

The returned slice replaces the old slice value

A slice value contains a pointer, length, and capacity. Delete can change the valid length but cannot rewrite the caller’s copy of that slice header. Code therefore needs to retain the returned value.

names := []string{"api", "cache", "worker"}
names = slices.Delete(names, 1, 2)

Ignoring the return value leaves names with its old length even though its backing array may already have been modified. The old slice value should be treated as stale after the call.

This also matters when two slice variables refer to the same backing array. A second alias can observe storage changes made during deletion even though its own length is unchanged.

values := []int{1, 2, 3, 4}
alias := values

values = slices.Delete(values, 1, 3)

fmt.Println(values) // [1 4]
_ = alias           // still refers to the modified backing array

When an independent original sequence is required, cloning before mutation establishes separate outer slice storage.

Current Delete clears obsolete tail slots

The standard library specifies that Delete zeroes the elements made obsolete at the end of the original slice range. This behavior is especially relevant for element types containing pointers.

Consider a slice of pointers:

type Record struct {
    ID int
}

a := &Record{ID: 1}
b := &Record{ID: 2}
c := &Record{ID: 3}

records := []*Record{a, b, c}
records = slices.Delete(records, 1, 2)

fmt.Println(records[0].ID, records[1].ID) // 1 3

After the suffix is shifted, obsolete slots between the new length and the old length are set to the element type’s zero value. For pointer elements that value is nil.

This tail-clearing contract differs from the traditional expression:

s = append(s[:i], s[j:]...)

That append-based form can leave old references in backing-array positions beyond the new length. slices.Delete has cleared obsolete tail elements since Go 1.22, reducing unintended retention of objects reachable only through those stale slots.

Bounds follow slice-expression validity

Delete requires s[i:j] to be a valid slice range. Invalid indices cause a panic, including a negative index, i > j, or j > len(s).

values := []int{1, 2, 3}
_ = slices.Delete(values, 1, 4) // panic

The function is therefore suited to indices already established as valid by surrounding logic. Inputs derived from external data need bounds validation when panic is not an acceptable failure mode.

Deleting the entire valid range is supported:

values := []int{1, 2, 3}
values = slices.Delete(values, 0, len(values))

fmt.Println(len(values)) // 0

When deletion produces an empty result, the result keeps the nil state of the input. A nil input deleted with the valid range [0:0] remains nil; an emptied non-nil slice remains non-nil.

One range avoids repeated suffix movement

The documented cost of Delete is proportional to the surviving suffix after index i. Removing several adjacent elements with separate calls can shift much of the same suffix repeatedly.

When the elements form one contiguous interval, a single range expresses the operation directly:

values = slices.Delete(values, start, end)

Scattered removal based on a predicate is a different shape. slices.DeleteFunc covers that case by retaining elements for which the predicate does not request deletion.

In-place mutation is the central constraint

slices.Delete gives contiguous slice removal a direct standard-library expression, but it remains an in-place operation. The backing array can be shared, the returned header defines the new valid length, and obsolete tail slots are cleared.

Those properties are more significant than the shorter syntax. Code that treats the pre-call slice as an independent snapshot can observe surprising mutations; code that treats deletion as ownership of the current slice value gets a compact operation with explicit range semantics and defined tail handling.