コンテンツにスキップ

Rustのエラー E0424: expected value, found module `self`(selfパラメータがない)

E0424 は、self パラメータを持たない関数の中で self と書いてしまった、という意味のエラーです。その関数には「自分自身」にあたる値がないので、Rust は self が何を指すのかわかりません。Java や C#、JavaScript では this がいつでも使えるので、そこから来た人がとくによくやるうっかりミスです。直し方はたいてい1語だけで、関数の引数リストに &self を足します。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。

error[E0424]: expected value, found module `self`
--> src/main.rs:7:32
|
6 | fn describe() {
| -------- this function doesn't have a `self` parameter
7 | 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 を直せば、こちらも消えます。

rustc がコードに付けた2つのラベルを見てみましょう。

  1. 6行目: fn describe() のかっこの中が空です。rustc は「この関数には self パラメータがない」(this function doesn’t have a self parameter)と指摘しています。
  2. 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 では、何も隠されていません。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 を付けてもかまいません。どちらでも大丈夫です。関数が明らかに「カートについての」処理なら、メソッドにしたほうが自然に読めます。

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` parameter
8 | 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を見てください。

  • E0425:逆パターンのうっかりミス。メソッドの中にいるのに、self.count ではなく count と書いてしまった。
  • E0599:「no method named … found」(そういう名前のメソッドがない)。self のない関数はドットで呼べないので、E0424 のすぐあとによく表示されます。
  • E0061:引数の数が違う。たとえば値を渡さずに Player::label() と呼んだとき。
  • E0596:&self を足したけれど、メソッドがフィールドを書き換えているので &mut self が必要。フィールドに代入した場合は E0594 になります。
  • コンパイルエラーの一覧:エラー解説ページの全リストです。

理解度チェック

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