Go言語のスライスのcopy関数とスライス操作の基本テクニック
生徒
「先生、Go言語のスライスって便利だけど、copy関数って何をするんですか?」
先生
「copy関数は、あるスライスの中身を別のスライスにコピーするための関数です。スライス同士でデータを移し替えるときに使います。」
生徒
「スライスを操作するときに他に覚えておくといいテクニックはありますか?」
先生
「はい、スライスの基本操作やよく使うテクニックも一緒に説明しますね!」
1. copy関数とは?
copy関数は、あるスライスの中身を「そのまま別のスライスへ写す」ための標準関数です。元データは変更されないので、バックアップを作ったり、加工用に別の入れ物を用意したいときに役立ちます。ファイルを複製してから編集するイメージに近いですね。
使い方はシンプルで「copy(コピー先, コピー元)」と書きます。戻り値として「実際に写せた要素数」が返ってくるため、「ちゃんとコピーできたか」を数で確認できます。まずは最小の例で動きを見てみましょう。
package main
import "fmt"
func main() {
src := []int{1, 2, 3} // コピー元(元データ)
dst := make([]int, len(src)) // コピー先(同じ大きさを用意)
n := copy(dst, src) // dstへ中身を写す
fmt.Println("コピーした要素数:", n)
fmt.Println("src:", src) // 元データはそのまま
fmt.Println("dst:", dst) // 複製されたデータ
}
このサンプルでは、srcは一切変わらず、dstだけが複製された内容になります。「安全に複製してから作業する」――これがcopyの基本的な役割です。
2. copy関数の基本的な使い方
ここでは、実際にcopy関数を動かしてみましょう。使い方はとても簡単で、「コピー先のスライス」と「コピー元のスライス」を指定するだけです。コピー先は、あらかじめ必要な要素数ぶんのサイズを確保しておく必要があります。そうしないと、入り切らない要素はコピーされません。
src := []int{1, 2, 3} // コピー元のスライス
dst := make([]int, 3) // コピー先のスライスを3つ分確保
n := copy(dst, src) // src → dstへコピー
fmt.Println("コピーした要素数:", n)
fmt.Println("src:", src)
fmt.Println("dst:", dst)
実行すると、srcのデータがそのままdstへ複製され、戻り値として「何個コピーできたか」が表示されます。元のsrcが変わらず、dstだけが書き換わっている様子を見ると、コピーが安全に行われていることが分かります。まずはこの基本形をしっかり覚えておくと、応用でも迷わず使えます。
生徒
「先生、コピーしたあとに src を変えても dst には影響しないんですか?」
先生
「そうです。copy は中身を『複製』するので、別々のスライスになります。だからバックアップを取りたいときにも便利ですよ。」
生徒
「なるほど!ファイルをコピーして編集するみたいな感覚ですね。」
3. copy関数のポイント
ここでは、初心者の方でもつまずきやすい「copy関数を使うときに気をつけたいポイント」を整理しておきましょう。特に、スライス同士でデータをコピーするときは、コピー元とコピー先の大きさや動きを理解しておくと安心です。
- コピーされる要素数は、コピー元とコピー先のスライスの「小さい方」に合わせてコピーされます。
- コピー先のスライスは、コピーしたい数ぶんの「サイズを事前に確保」しておく必要があります。
copyは、新しいスライスを自動で作るのではなく、すでにあるスライスに上書きします。
たとえばコピー先が小さいと、入り切らない要素はコピーされません。逆に、コピー先を広めに作っておけば、必要なぶんしっかり保存できます。実際に短いサンプルで確認してみましょう。
src := []int{10, 20, 30}
dst := make([]int, 2) // コピー先が小さい
copy(dst, src)
fmt.Println(dst) // [10 20] → 3つの要素中、2つ分だけコピー
この例のように、copy関数は「できる範囲だけコピーする」という動きになります。「全部コピーできているかな?」と確認したいときは、戻り値の数を見て判断できます。
4. スライス操作の基本テクニック
スライスは配列のように使えますが、動的にサイズが変えられます。ここではよく使う操作を紹介します。
4-1. スライスの一部分を取り出す(スライス操作)
スライスから特定の範囲だけ取り出したいときは次のように書きます。
nums := []int{10, 20, 30, 40, 50}
sub := nums[1:4] // インデックス1から3まで取り出す(4は含まない)
fmt.Println(sub) // [20 30 40]
スライスの範囲指定は [開始:終了] で、開始は含み、終了は含みません。
4-2. スライスの先頭から指定位置まで取り出す
start := nums[:3] // インデックス0から2まで
fmt.Println(start) // [10 20 30]
4-3. スライスの指定位置から最後まで取り出す
end := nums[2:] // インデックス2から最後まで
fmt.Println(end) // [30 40 50]
5. copy関数とスライス操作を使った応用例
copy関数でスライスの一部分だけを別のスライスにコピーする例を見てみましょう。
src := []int{100, 200, 300, 400, 500}
dst := make([]int, 2)
copy(dst, src[1:3]) // srcのインデックス1から2までをdstにコピー
fmt.Println(dst) // [200 300]
ここではsrc[1:3]でスライスの一部分を取り出し、それをdstにコピーしています。
6. スライスの容量と長さについて
スライスには「長さ」と「容量」があります。長さは現在の要素数、容量は拡張できる最大要素数を表します。
nums := []int{1, 2, 3, 4, 5}
sub := nums[1:3]
fmt.Println(len(sub)) // 長さは2
fmt.Println(cap(sub)) // 容量は4
容量はスライスが元の配列からどこまで続いているかを示します。appendで容量を超えると新しい配列が作られます。
7. スライスのコピーと参照の違い
スライスは配列の一部を参照しているので、直接代入すると中身を共有します。これを避けるにはcopyで中身を複製しましょう。
a := []int{1, 2, 3}
b := a // aとbは同じ配列を参照
b[0] = 100
fmt.Println(a) // [100 2 3] 変更がaにも反映される
c := make([]int, len(a))
copy(c, a) // cはaのコピー
c[0] = 200
fmt.Println(a) // [100 2 3] 変更なし
fmt.Println(c) // [200 2 3]
まとめ
Go言語のスライスとcopy関数について学んできましたが、改めて振り返ってみると、単にコピーをするだけの関数だと思った人ほど、実際に使ってみると便利さを強く感じるはずです。スライスという仕組みは、配列を柔軟に扱うために生まれたとても便利な仕掛けであり、プログラミング初心者でも理解しやすく、それでいて実務的な開発でも頻繁に使われる重要な概念です。
今回のページでは、スライスの基本的な操作やcopy関数の使い方、参照による影響や部分的な抽出など、初心者でも無理なく理解できるように、段階を踏みながら丁寧に整理してきました。
スライスは内部で配列を参照しているため、別の変数に代入しても同じ配列を共有するという点がとても大切です。
初心者でも覚えやすいルールとして、「copyはコピー先のスライスに上書きする」という点がとても重要です。新しくスライスを作るわけではなく、あらかじめ用意しておいたスライスに中身を写すという仕組みが基本になります。
さらに、スライスの基本操作では、一部分だけを切り出して新しいスライスを作ることができました。この機能は配列にはない柔軟な要素であり、データの中から必要な部分だけを取り出したいときにとても重宝します。
長さは「現在入っている要素の数」、容量は「今後増やせる余裕」を示していました。
それでは、最後に簡単なサンプルプログラムを使いながら、今回学んだ内容をあらためて確認してみましょう。
スライスとcopy関数を使った確認用サンプル
package main
import "fmt"
func main() {
prices := []int{150, 200, 300, 400}
backup := make([]int, len(prices))
n := copy(backup, prices)
fmt.Println("コピーした要素数:", n)
fmt.Println("元のスライス:", prices)
fmt.Println("コピーしたスライス:", backup)
prices[0] = 999
fmt.Println("元を変更:", prices)
fmt.Println("コピーは変化なし:", backup)
}
ここでは、商品価格のリストをコピーし、元のスライスを変更してもコピー先が影響を受けないことを確認しています。このような処理は、データ分析や一時的な計算のときにとても役に立ちます。実際に数字を変えて動かすと、違いがより分かりやすくなるでしょう。 また、スライスの一部分を取り出すサンプルも見てみましょう。
一部分だけをコピーするサンプル
package main
import "fmt"
func main() {
numbers := []int{10, 20, 30, 40, 50}
part := make([]int, 2)
copy(part, numbers[1:3])
fmt.Println("元のスライス:", numbers)
fmt.Println("コピーした一部分:", part)
}
このように、スライスの一部分を別のスライスにコピーすることで、必要なデータだけをきれいに抽出できます。とても単純な機能ですが、経験を積むほど、この操作の便利さが分かってきます。
生徒
「先生、今日の内容を振り返ってみると、思ったよりもスライスって便利なんですね。配列と違って長さを気にせず使える感じがすごくありがたいです。」
先生
「そうですね。特に、現実のデータは数が変わりやすいので、スライスの柔軟性はとても役に立ちますよ。copy関数を使えば、安全に複製して、元のデータを壊さずに加工できます。」
生徒
「直接代入すると同じところを参照してしまうというところが、最初は怖いと思ったけど、copyを使えば安心ですね。」
先生
「そのとおりです。プログラムは目に見えないところでデータが動いているので、参照の仕組みを知らないとトラブルになりがちですが、ちゃんと理解していれば何も心配いりません。」
生徒
「部分的なコピーもすごく便利ですね。必要なところだけ抜き出して、新しい処理をすることが簡単にできそうです。」
先生
「はい。今回の内容は、どのレベルの開発でも役に立つ大切な考え方ですから、ぜひしっかり覚えておいてください。」
この記事を読んだ人からの質問
プログラミング初心者からのよくある疑問/質問を解決します
Go言語のスライスと配列の違いは何ですか?初心者でも分かるように教えてください
Go言語のスライスは動的にサイズを変更できる柔軟な仕組みで、配列よりも使いやすいデータ構造です。配列は固定長で要素数を後から変えられませんが、スライスは必要に応じて長さを変えられるため、実務のプログラミングでよく使われます。Go言語初心者でもスライスのほうが扱いやすいという理由で、多くの入門書やWeb開発でもスライスが採用されています。
Go言語のcopy関数は何のために使うのですか?copy関数の使いどころを教えてほしいです
Go言語のcopy関数は、スライスの中身を別のスライスへ複製するために使います。コピー先とコピー元が別になるため、元のスライスを変更してもコピー先に影響が出ないというメリットがあります。データのバックアップを作りたいときや、元のデータを壊さずに加工したいときにcopy関数はとても便利です。
【超入門】ゼロから始めるGo言語プログラミング:最速で「動くアプリ」を作るマンツーマン指導
「プログラミングの仕組み」が根本からわかる。Go言語でバックエンド開発の第一歩を。
本講座を受講することで、単なる文法の暗記ではなく、「プログラムがコンピュータの中でどう動いているか」という本質的な理解につながります。シンプルながら強力なGo言語(Golang)を通じて、現代のバックエンドエンジニアに求められる基礎体力を最短距離で身につけます。
具体的な開発内容と環境
【つくるもの】
ターミナル(黒い画面)上で動作する「対話型計算プログラム」や、データを整理して表示する「ミニ・ツール」をゼロから作成します。自分の書いたコードが形になる感動を体験してください。
【開発環境】
プロの現場でシェアNo.1のVisual Studio Code (VS Code)を使用します。インストールから日本語化、Go言語用の拡張機能設定まで、現場基準の環境を一緒に構築します。
この60分で得られる3つの理解
「なぜ動くのか」という設定の仕組みを理解し、今後の独学で詰まらない土台を作ります。
データの種類やメモリの概念など、他言語にも通じるプログラミングの本質を学びます。
ただ動くだけでなく、誰が見ても分かりやすい「綺麗なコード」を書くための考え方を伝授します。
※本講座は、将来的にバックエンドエンジニアやクラウドインフラに興味がある未経験者のためのエントリー講座です。マンツーマン形式により、あなたの理解度に合わせて進行します。
初めてのGo言語を一緒に学びましょう!