Rustのエラー E0424: expected value, found module `self`(selfパラメータがない)
E0424 は、self パラメータを持たない関数の中で self と書いてしまった、という意味のエラーです。その関数には「自分自身」にあたる値がないので、Rust は self が何を指すのかわかりません。Java や C#、JavaScript では this がいつでも使えるので、そこから来た人がとくによくやるうっかりミスです。直し方はたいてい1語だけで、関数の引数リストに &self を足します。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。
エラーメッセージ
Section titled “エラーメッセージ”error[E0424]: expected value, found module `self` --> src/main.rs:7:32 |6 | fn describe() { | -------- this function doesn't have a `self` parameter7 | println!("Player: {}", self.name); // ❌ どのプレイヤー? | ^^^^ `self` value is a keyword only available in methods with a `self` parameter |help: add a `self` receiver parameter to make the associated `fn` a method |6 | fn describe(&self) { | +++++
error[E0599]: no method named `describe` found for struct `Player` in the current scope --> src/main.rs:13:10 |13 | hero.describe(); | ^^^^^^^^ this is an associated function, not a method「expected value, found module self」は「値が来るはずの場所に、モジュールの self があった」という意味です。ラベルは「self という値は、self パラメータを持つメソッドの中でだけ使えるキーワードです」と説明し、help は「self を受け取るパラメータを足して、関連関数をメソッドにしてはどうか」と提案しています。2つ目の E0599 は「Player に describe というメソッドがない。これはメソッドではなく関連関数だ」という意味です。
このエラーが出るのは、次のプログラムです。
struct Player { name: String,}
impl Player { fn describe() { println!("Player: {}", self.name); // ❌ どのプレイヤー? }}
fn main() { let hero = Player { name: String::from("Ferris") }; hero.describe();}2つ目のエラー E0599 は、1つ目のエラーの巻き添えで出ているだけです。E0424 を直せば、こちらも消えます。
エラーの意味
Section titled “エラーの意味”rustc がコードに付けた2つのラベルを見てみましょう。
- 6行目:
fn describe()のかっこの中が空です。rustc は「この関数にはselfパラメータがない」(this function doesn’t have aselfparameter)と指摘しています。 - 7行目:
self.nameは「このメソッドを呼び出したプレイヤーの名前」を求めています。でもdescribeはプレイヤーを受け取っていないので、ここでのselfは何も指していません。
impl ブロックの中にあって self パラメータを持たない関数は、関連関数(associated function)です。特定の1つの値ではなく、型そのもの(Player::describe())に属します。self パラメータを持つ関数はメソッド(method)で、self と書けるのはメソッドだけです。
「ここに自分の名前を書いてください」と書かれた用紙を思い浮かべてください。この文は、だれかが用紙を手に持っていて初めて意味を持ちます。describe() は、だれも手に持っていない用紙のようなものです。
では、なぜ「found module self」(モジュールの self があった)と言われるのでしょうか。Rust の self には、もう1つの意味があります。use self::helpers; のように、今いるモジュールを指す意味です。self という値がないと、rustc はこちらの意味で解釈し、「モジュールは値ではない」と文句を言うのです。「module」という言葉は気にしなくてかまいません。本当の問題は、パラメータが足りないことです。
Rust がこうする理由
Section titled “Rust がこうする理由”Rust では、何も隠されていません。Java や C# では、static でないメソッドにはすべて目に見えない this があります。Rust では、self を最初のパラメータとして自分で書きます。しかも、メソッドがその値をどう受け取るのか(見るだけか、書き換えるか、所有権ごと受け取るか)もあわせて書きます。この選び方は借用のルールの一部なので、目に見える形で書く必要があるのです。
おかげで、コードを読む人にもコンパイラにも、メソッドが値に何をするのかがはっきり伝わります。fn summary(&self) は何も変えないと約束しています。fn add(&mut self, ...) は値を変えることを知らせています。fn checkout(self) は値を使い切ります。そしてコンパイラが、その約束が守られているかをチェックしてくれます。メソッドと impl ブロックについては、構造体のページで説明しています。
直し方1:&self、&mut self、self を足す
Section titled “直し方1:&self、&mut self、self を足す”関数がすでにある1つの値を相手に動くはずのときは、これを選びましょう。ほとんどの場合、これが正しい直し方です。rustc が提案するパラメータを足して、ドットで呼び出します。
struct Player { name: String,}
impl Player { fn describe(&self) { println!("Player: {}", self.name); }}
fn main() { let hero = Player { name: String::from("Ferris") }; hero.describe();}実行結果:
Player: Ferris(エラーになる例と比べやすいように、ここでは表示する文字列を英語のままにしています。)
rustc はいつも &self を提案しますが、実は3種類あります。メソッドがすることに合わせて選びましょう。
| パラメータ | そのメソッドは… | よくある名前 |
|---|---|---|
&self |
値を読むだけ | summary、len、is_empty、describe |
&mut self |
値を書き換える | add、push、reset、level_up |
self |
値を使い切る(所有権を受け取る) | into_...、finish、build、checkout |
1つの型に3つともそろえると、こうなります。
struct Order { items: Vec<String>,}
impl Order { fn summary(&self) -> String { format!("{}個の商品: {}", self.items.len(), self.items.join("、")) }
fn add(&mut self, item: &str) { self.items.push(item.to_string()); }
fn checkout(self) -> Vec<String> { self.items }}
fn main() { let mut order = Order { items: Vec::new() }; order.add("コーヒー"); order.add("ベーグル"); println!("{}", order.summary()); let packed = order.checkout(); // ここで order は使い切られる println!("袋に入れた商品: {:?}", packed);}実行結果:
2個の商品: コーヒー、ベーグル袋に入れた商品: ["コーヒー", "ベーグル"]&self を選んだのにメソッドがフィールドを書き換えていると、コンパイラがすぐに教えてくれます(E0594 の「cannot assign to self.count, which is behind a & reference」、push のような呼び出しなら E0596)。そのときは &mut self に変えましょう。迷ったら、まず &self から始めてください。self を使う場面はあまり多くありません。
直し方2:型::メソッド(&値) の形で呼ぶ
Section titled “直し方2:型::メソッド(&値) の形で呼ぶ”self パラメータを持つ関数は、2通りの書き方で呼び出せます。hero.label() と Player::label(&hero) はまったく同じ意味で、ドットの前に置く値が1つ目の引数になります。ふだんはドットの形で書くことがほとんどです。型:: の形は、map などにメソッドそのものを渡したいときに便利です。
struct Player { name: String, level: u32,}
impl Player { fn label(&self) -> String { format!("{}(レベル{})", self.name, self.level) }}
fn main() { let hero = Player { name: String::from("Ferris"), level: 3 }; println!("{}", hero.label()); println!("{}", Player::label(&hero)); // 同じ呼び出し
let team = vec![hero, Player { name: String::from("Corro"), level: 5 }]; let labels: Vec<String> = team.iter().map(Player::label).collect(); println!("{:?}", labels);}実行結果:
Ferris(レベル3)Ferris(レベル3)["Ferris(レベル3)", "Corro(レベル5)"]うまくいかないのは、値を渡さずに Player::label() と書くことです。メソッドには必ず self が必要です。省くと E0061 の「this function takes 1 argument but 0 arguments were supplied」(この関数は引数を1つ取るのに、0個しか渡されていない)になります。
直し方3:関数に必要なものを引数で渡す
Section titled “直し方3:関数に必要なものを引数で渡す”まだ値がないとき、または関数がそもそも impl ブロックの中にないときは、これを選びます。
典型的なのはコンストラクタです。new は値を作る関数なので、読み取る元になる self がありません。new には最初のデータをふつうの引数として渡し、Self { ... } で構造体を組み立てます。
struct Counter { count: u32, step: u32,}
impl Counter { fn new(step: u32) -> Self { Self { count: 0, step } }
fn tick(&mut self) { self.count += self.step; }}
fn main() { let mut counter = Counter::new(5); counter.tick(); counter.tick(); println!("カウント: {}", counter.count);}実行結果:
カウント: 10大文字と小文字に注意してください。Self(大文字の S)は型なので、Self { ... } で新しい値を作れます。self(小文字の s)は、メソッドを呼び出した値です。
同じ考え方で、impl ブロックの外にあるふつうの関数も直せます。その場合、rustc の言い方は少し変わり、「this function can’t have a self parameter」(この関数は self パラメータを持てない)となります。代わりに、構造体を参照で渡しましょう。
struct Cart { items: Vec<u32>,}
fn total(cart: &Cart) -> u32 { cart.items.iter().sum()}
// 呼び出し方: total(&cart)あるいは、直し方1のように total を impl Cart の中に移して &self を付けてもかまいません。どちらでも大丈夫です。関数が明らかに「カートについての」処理なら、メソッドにしたほうが自然に読めます。
よく出会う場面
Section titled “よく出会う場面”new の中で。 fn new() -> Self の中で self.count = 0; と書くと E0424 になります。rustc は fn new(&self) -> Self を提案しますが、これは意味が通りません(カウンタを作るのにカウンタが必要になってしまいます)。直し方:直し方3のように、Self { count: 0 } で値を組み立てます。
関連関数の中のクロージャで。 問題はクロージャではなく、それを囲んでいる関数のほうです。
error[E0424]: expected value, found module `self` --> src/main.rs:8:35 |7 | fn sale_prices(prices: &[u32]) -> Vec<u32> { | ----------- this function doesn't have a `self` parameter8 | prices.iter().map(|p| p - self.discount).collect() | ^^^^ `self` value is a keyword only available in methods with a `self` parameterメッセージの中身は、さきほどと同じく「この関数には self パラメータがない」です。直し方:fn sale_prices(&self, prices: &[u32]) としてメソッドにするか、fn sale_prices(prices: &[u32], discount: u32) として値を引数で渡します。詳しくはクロージャを見てください。
main やヘルパー関数の中で。 impl ブロックの外では、self はけっして使えません。直し方:構造体を引数で渡す(total(&cart))か、その構造体のメソッドを呼びます(cart.total())。
C# や Java の「static」メソッドの感覚で書いたとき。 self のない関連関数は、Rust 版の static メソッドです。インスタンスのメソッドにしたいなら、self パラメータを自分で足す必要があります。詳しくはC#開発者のためのRustを見てください。
関連するエラー
Section titled “関連するエラー”- E0425:逆パターンのうっかりミス。メソッドの中にいるのに、
self.countではなくcountと書いてしまった。 - E0599:「no method named … found」(そういう名前のメソッドがない)。
selfのない関数はドットで呼べないので、E0424 のすぐあとによく表示されます。 - E0061:引数の数が違う。たとえば値を渡さずに
Player::label()と呼んだとき。 - E0596:
&selfを足したけれど、メソッドがフィールドを書き換えているので&mut selfが必要。フィールドに代入した場合は E0594 になります。 - コンパイルエラーの一覧:エラー解説ページの全リストです。
理解度チェック
Section titled “理解度チェック”理解度チェック
1. impl Player の中の fn heal() { self.hp += 10; } が E0424 になります。いちばんよい直し方はどれですか?
2. fn new() -> Self の中で self.name が使えないのはなぜですか?
3. Player に fn label(&self) -> String が定義されています。hero.label() と同じ意味になる呼び出しはどれですか?
スコア: 0 / 3
さらに詳しく
Section titled “さらに詳しく”- E0424 — Rust error codes index(英語):このエラーの公式の説明です。短い例も載っています。
- メソッド記法 — The Rust Programming Language 日本語版:
&self、&mut self、selfと、newのような関連関数を説明している章です。 - メソッド — Rust By Example 日本語版:関連関数とメソッドを並べて、実行できる例で紹介しています。
selfキーワード — 標準ライブラリのドキュメント(英語):メソッドのレシーバーと、今いるモジュールという、selfの2つの意味がわかります。- Associated items — The Rust Reference(英語):メソッド、レシーバー、関連関数の正確なルールです。