本文へ移動
cccskills
無料GitHub で公開

rust-ownership-discipline

Rust のコードを書く・直す・レビューするときに使う。所有権、借用、Clone/Copy derive、文字列型 (`String`, `&str`, `Cow`, `'static str`) の選び方を、安易な複製や過剰な lifetime 固定に寄せず、実際のデータ所有者と呼び出し境界に合わせて判断するためのスキル。

インストール方法を見る

含まれるファイル(2)

  • SKILL.md7.6 KB
  • agents/openai.yaml281 B

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Rust の所有権判断の規律

基本方針

Rust では、所有権と lifetime を「コンパイルを通すための飾り」ではなく、データの責務境界として扱う。

まず自然な所有者、借用で済む範囲、値を複製する理由を確認する。Clone / Copy / 'static は便利だから付けるのではなく、API とデータの寿命を正しく表す場合だけ使う。

このスキルは、The Rust Book、標準ライブラリ docs、Rust API Guidelines、Clippy の所有権関連 lint から実装判断に必要な部分だけを蒸留したものとして扱う。通常の実装やレビューでは外部 URL を読みに行かず、この本文の判断基準を使う。Rust の仕様変更や判断基準そのものを更新するときだけ、元資料を確認する。

Clone / Copy derive

Clone / Copy を derive する前に、その型の意味で複製が自然かを確認する。

Copy は、代入や引数渡しで move ではなく安い複製になることを API として公開する。実装できる型は基本的に Copy にしてよいが、将来 Copy でなくなる可能性がある公開型では、破壊的変更を避けるために省く判断も妥当。

Copy を許可しやすい型:

  • u64 や Uuid などの小さな値に意味を付けるだけの newtype
  • 座標、サイズ、期間、フラグのように値そのものが小さく、複製しても同じ概念を表す値オブジェクト
  • 所有権移動を意識させるほうが不自然な、単純で不変な識別子

Copy にしてはいけない型:

  • Drop を実装する型、または将来リソース解放を持つ可能性が高い型
  • String、Vec<T>、PathBuf、OsString、file handle、socket、lock guard など、heap buffer や外部リソースの管理責任を持つ型
  • &mut T を含む型。可変参照の複製は aliasing ルールに反する

Clone を安易に derive しない型:

  • 集約 struct、状態を持つ domain object、外部リソースや大きな Vec / String を含む型
  • 複製すると「別インスタンスなのか、同じもののコピーなのか」が曖昧になる型
  • 呼び出し側の所有権設計を避けるためだけに clone されている型

必要になってから Clone を足す。足す場合も、呼び出し側で本当に別の所有値が必要か、借用や参照渡しで表せないかを先に確認する。clone() は heap data の複製や任意の処理を伴いうるため、可視的なコストとして扱う。

// OK: 値そのものが識別子で、複製しても責務が増えない。
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
struct UserId(u64);

// 要確認: clone で同じ集約のコピーを作る意味が曖昧になりやすい。
#[derive(Clone)]
struct User {
    id: UserId,
    name: String,
    permissions: Vec<String>,
}

文字列型

文字列は、誰が所有し、どれくらい生きるかで選ぶ。

  • 値として保持する struct field は、通常 String を使う
  • 一時的に読むだけの関数引数は、通常 &str を受け取る
  • literal やプロセス全体で固定の定数だけを表す場合に限り、&'static str を使う
  • 借用と所有の両方を自然に受けたい場合だけ、Cow<'a, str> や generic を検討する

'static str は「長く生きてほしい文字列」ではない。設定値、入力、DB/API 由来の値、後から組み立てる値を 'static に寄せない。必要なら所有する String にする。

型選択の初期値:

場面まず選ぶ型理由
struct が値を保持するString所有者が明確で、不要な lifetime を API 外へ漏らさない
関数が一時的に読む&strString、&str、slice 由来の値を広く受けられる
固定メッセージや定数&'static strbinary に埋め込まれた literal の寿命を正しく表す
必要なら借用、必要なら所有Cow<'a, str>書き換えや所有が必要なときだけ clone できる
複数 thread で同じ文字列を共有Arc<str>所有値を共有し、文字列本体の重複を避ける

&str を受けた後、すぐ to_owned() / to_string() して処理するなら、&str を選んだ意味が失われている可能性が高い。所有化してよいのは、値を struct に保存する、関数の外へ返す、別 thread / async task へ渡す、または API が所有値を要求する場合に限る。読むだけ、検証するだけ、分岐するだけ、別の借用 API へ渡すだけなら、借用のまま処理する。

// OK: 入力を読むだけなら &str のまま処理する。
fn is_valid_name(name: &str) -> bool {
    let name = name.trim();
    !name.is_empty() && name.chars().all(|c| c.is_alphanumeric() || c == '_')
}

// OK: 正規化した新しい文字列を返すので、戻り値として所有する。
fn normalized_name(name: &str) -> String {
    name.trim().to_ascii_lowercase()
}

// OK: 設定値を保持する型は所有する。
struct Config {
    profile_name: String,
}

// 避ける: 呼び出し側に String を強制する理由がない。
fn normalize_name_bad(name: &String) -> String {
    name.trim().to_lowercase()
}

// 避ける: 借用で読めるのに、先頭で所有化している。
fn is_valid_name_bad(name: &str) -> bool {
    let name = name.to_owned();
    !name.trim().is_empty()
}

Path / PathBuf、OsStr / OsString、slice / Vec<T> でも同じ考え方を使う。読むだけの引数は借用型、保持する field や返り値は所有型を基本にする。

実装時の確認

Rust の差分を書いた後、次を確認する。

  • Clone / Copy derive が、型の意味として自然か
  • .clone() が、所有値を本当に複数箇所で持つ必要から出ているか
  • to_owned() / to_string() が、保存、返却、所有値を要求する API 呼び出しのために必要か
  • clone で borrow checker との向き合いを避けていないか
  • struct field に &str や &'static str を入れて、不要に lifetime を外へ漏らしていないか
  • API が呼び出し側に不自然な lifetime や所有権を強制していないか
  • String を使えば素直な箇所を、過剰に &'static str、generic、Cow へ寄せていないか

迷う場合は、まず所有する String と借りる &str の素直な形で書く。性能上の理由や API 境界の理由が具体的に出てから、より細かい型へ寄せる。

出典メモ

この節はスキル更新時の確認先であり、通常の実装中に毎回読む必要はない。

  • The Rust Book: ownership、references and borrowing、String / Clone / Copy の基本
  • std::marker::Copy: Copy にできない型、Copy が public API であること
  • std::borrow::Cow: clone-on-write と borrowed / owned の使い分け
  • std::string::String: String の heap buffer、length、capacity
  • Rust API Guidelines: 標準 conversion trait と予測しやすい API 設計
  • Clippy ptr_arg / redundant_clone: &String より &str、不要な clone の検出

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

add-npm-package

無料日本語概要

modules/npm/packages/ に新しい npm/pnpm パッケージを追加するスキル

rito528/dotfiles22026年10月12日 更新

add-script

無料日本語概要

install/ 配下に新しいセットアップ用シェルスクリプトを追加するスキル

rito528/dotfiles22026年10月12日 更新

create-issue

無料日本語概要

GitHub issue を新規作成・下書きするとき、または既存 issue の本文を書き直すときに必ず使うスキル。「issue を作って」「issue 化して」「これを issue にまとめて」のような依頼で発火し、`gh issue create` を直接呼ぶ前に必ず参照する。人間が合意した内容だけを、意味を足さずに issue へ残すための基準を定める。

rito528/dotfiles22026年10月12日 更新

create-pr

無料日本語概要

GitHub に Pull Request を作成するスキル。ユーザーが「PRを作って」「プルリクエストを出して」「PR作成して」と言ったとき、またはコミット済みの変更をレビューに出したいときに使用する。

rito528/dotfiles22026年10月12日 更新

domain-design-safety-discipline

無料日本語概要

ドメインモデル、可視性、操作可能性、不変条件、層境界、API 設計に関わる実装時に使う。不正な状態や誤った呼び出し方を、型、関数の境界、ドメイン語彙で表し、正しい使い方が自然になる設計へ寄せるためのスキル。

rito528/dotfiles22026年10月12日 更新

ensure-branch

無料日本語概要

コミットやPR作成の前に、作業ブランチが適切かどうかを確認・切り替えるスキル。git-commit や create-pr からサブルーチンとして呼び出される。

rito528/dotfiles22026年10月12日 更新

rito528 のスキルをすべて見る

このスキルの問題を報告する