Skip to content
cccskills
FreePublished on GitHub

rust-ownership-discipline

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

The English summary is being prepared. Showing the original description.

See how to install

Files included (2)

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

SKILL.md (original)

Review the instructions the agent will receive before you install.

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 の検出

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

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

The English summary is being prepared. Showing the original description.

rito528/dotfiles2Updated Oct 19, 2026

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

The English summary is being prepared. Showing the original description.

rito528/dotfiles2Updated Oct 19, 2026

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

The English summary is being prepared. Showing the original description.

rito528/dotfiles2Updated Oct 19, 2026

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

The English summary is being prepared. Showing the original description.

rito528/dotfiles2Updated Oct 19, 2026

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

The English summary is being prepared. Showing the original description.

rito528/dotfiles2Updated Oct 19, 2026

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

The English summary is being prepared. Showing the original description.

rito528/dotfiles2Updated Oct 19, 2026

See all skills by rito528

Report a problem with this skill