Rustのエラー E0615: attempted to take value of method(メソッドをフィールドのように使った)
E0615 は、かっこを付けずに value.something と書いたけれど、something はフィールドではなくメソッドだった、という意味のエラーです。Rust のメソッドは、引数がなくても () を付けて呼んだときにだけ実行されます。C#、JavaScript、Python から来た人がとてもよく出会うエラーです。これらの言語では .Count や .length、プロパティがふつうのフィールドのように見えるからです。直し方は、ほとんどの場合たった2文字です。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。
エラーメッセージ
Section titled “エラーメッセージ”error[E0615]: attempted to take value of method `len` on type `String` --> src/main.rs:3:46 |3 | println!("{} has {} letters", name, name.len); // ❌ `len` はメソッド | ^^^ method, not a field |help: use parentheses to call the method |3 | println!("{} has {} letters", name, name.len()); // ❌ `len` はメソッド | ++「attempted to take value of method len on type String」は「String 型のメソッド len を値として取り出そうとした」という意味です。method, not a field は「フィールドではなくメソッド」、help は「かっこを付けてメソッドを呼びましょう」と教えてくれています。
このエラーが出るのは、次のプログラムです。
fn main() { let name = String::from("Ferris"); println!("{} has {} letters", name, name.len); // ❌ `len` はメソッド}エラーの意味
Section titled “エラーの意味”見出しは後ろから読むとわかりやすいです。on type String は Rust が調べた型で、method len はそこで見つけたものです。つまり、Rust は len をちゃんと見つけています。ただ、それはフィールドのように読める値ではなく、実行しなければならないコードなのです。
method, not a field というラベルと、help の () の下にある ++ が、まさにそれを伝えています。かっこを付ければ解決です。
自動販売機を思い浮かべてください。フィールドは機械に印刷されたラベル(「12番の自販機」)のようなもので、読むだけで済みます。メソッドはボタン(「おつりを表示」)のようなもので、押すまでは何も起きません。name.len はボタンを指さしているだけで、name.len() でボタンを押すことになります。
Rust がこうする理由
Section titled “Rust がこうする理由”Rust では、フィールドとメソッドははっきり分かれています。フィールドは、構造体の中にしまってあるデータです。メソッドは、impl ブロックに書かれた、self を受け取る関数です。フィールドは value.field で読み、メソッドは value.method() で呼びます。詳しくはメソッドで振る舞いを追加するを見てください。
多くの言語では、プロパティがこの境目をあいまいにしています。C# の list.Count や JavaScript の text.length はフィールドのように見えますが、裏ではコードが動いています。Rust には、あえてプロパティがありません。() があればコードが実行される(時間がかかるかもしれない)、なければしまってあるデータを読むだけ、とひと目でわかります。隠れているものは何もありません。
構造体には、非公開の balance フィールドと公開の balance() メソッドのように、同じ名前のフィールドとメソッドを両方持たせることさえできます。これができるのは、Rust がどちらのつもりかを推測しないからです。かっこがなければフィールド、かっこがあればメソッドです。
直し方1:かっこを付ける
Section titled “直し方1:かっこを付ける”ほとんどの場合はこれです。help: の行のとおりに、メソッドを呼びましょう。自分で書いたメソッド(ショッピングカートの total など)でも、len、is_empty、trim、count のような標準のメソッドでも、やり方は同じです。
struct Item { price: u32, // セント単位 qty: u32,}
struct Cart { items: Vec<Item>,}
impl Cart { fn total(&self) -> u32 { self.items.iter().map(|i| i.price * i.qty).sum() }}
fn main() { let name = String::from("Ferris"); println!("{} has {} letters", name, name.len()); // ✅ 呼んでいる
let cart = Cart { items: vec![Item { price: 250, qty: 2 }, Item { price: 100, qty: 1 }], }; if !cart.items.is_empty() { println!("合計: {}セント", cart.total()); // ✅ 呼んでいる }}実行結果:
Ferris has 6 letters合計: 600セント(エラーになる例と比べやすいように、1行目の文字列は英語のままにしています。)
メソッドに引数が必要なときは、rustc が _ という目印で引数の場所を教えてくれます。s.contains なら、help には s.contains(_) と表示されます。探したいものを入れて、s.contains("world") のように書きましょう。メソッドに何が必要かわからないときは、次に出るエラー(E0061)が引数の数を教えてくれます。
直し方2:value.method ではなく、メソッドを関数として渡す
Section titled “直し方2:value.method ではなく、メソッドを関数として渡す”今すぐメソッドを呼びたいのではなく、.map() や sort_by_key のようなものに渡したいときは、これを選びます。.map(words[0].len) と書いても E0615 になります。よい方法は2つあります。メソッドを呼ぶ短いクロージャを書くか、メソッドのフルパス String::len を関数名のように使うかです。
fn main() { let words = vec![String::from("crab"), String::from("ferris"), String::from("rust")];
// 要素ごとにメソッドを呼ぶクロージャ let lengths: Vec<usize> = words.iter().map(|w| w.len()).collect(); println!("{:?}", lengths);
// メソッドのパスは関数名のように使える let same: Vec<usize> = words.iter().map(String::len).collect(); println!("{:?}", same);
let mut sorted = words.clone(); sorted.sort_by_key(|w| w.len()); println!("{:?}", sorted);}実行結果:
[4, 6, 4][4, 6, 4]["crab", "rust", "ferris"](文字列の長さを表示する例なので、単語は英語のままにしています。日本語の文字は1文字で3バイトになるからです。)
ここで String::len が使えるのは、iter() が &String の要素を返し、String::len が &String を受け取るからです。型がぴったり合わないときは、クロージャ |w| w.len() を使えば必ずうまくいきます。
直し方3:本当にしまっておくデータなら、フィールドにする
Section titled “直し方3:本当にしまっておくデータなら、フィールドにする”エラーが自分の構造体で出ていて、その値はただしまっておけばいいと気づいたときは、これを選びます。これは設計の問題です。フィールドにするか、メソッドにするか?
- 一度決まったら、ほかの何にも左右されない値はフィールドにします。
id、name、created_at(作成日)などです。 - ほかのデータから計算する値はメソッドにします。商品から出す
total、幅と高さから出すarea、中身から出すlenなどです。
total をフィールドとしてしまっておくと、誰かが商品を追加して更新し忘れた瞬間に、値が古くなってしまいます。total() メソッドなら毎回計算し直すので、間違うことがありません。次の例では id がフィールドで total がメソッドです。注文が変わっても、メソッドの値が正しいままなのがわかります。
mod shop { pub struct Order { pub id: u32, // しまっておく値:一度決めたら order.id で読む prices: Vec<u32>, // 非公開:メソッドを通してだけ変更する }
impl Order { pub fn new(id: u32) -> Self { Order { id, prices: Vec::new() } }
pub fn add(&mut self, price: u32) { self.prices.push(price); }
// 計算する値:いつも prices と一致し、古くならない pub fn total(&self) -> u32 { self.prices.iter().sum() }
pub fn item_count(&self) -> usize { self.prices.len() } }}
use shop::Order;
fn main() { let mut order = Order::new(17); order.add(250); order.add(100); println!("注文{}: {}点、{}セント", order.id, order.item_count(), order.total());
order.add(50); println!("注文{}: {}点、{}セント", order.id, order.item_count(), order.total());}実行結果:
注文17: 2点、350セント注文17: 3点、400セント多くのライブラリで見かける中間的な方法もあります。非公開のフィールドと、同じ名前の公開メソッド(「ゲッター」)の組み合わせです。ほかのコードは acc.balance() で値を読めますが、直接書き換えることはできません。このとき、モジュールの外から acc.balance と書くと、少し違うエラー E0616「field balance of struct Account is private」(構造体 Account のフィールド balance は非公開)が出て、a method `balance` also exists, call it with parentheses(balance というメソッドもあるので、かっこを付けて呼んでください)というヒントが付きます。直し方は同じで、() を付けます。詳しくは何もしなければすべて非公開を見てください。
よく出会う場面
Section titled “よく出会う場面”ほかの言語のくせ。 name.len、list.is_empty、text.trim、words.iter().count は、どれも E0615 になります。Rust ではどれもメソッドなので、どれにも () が必要です。直し方:name.len()、list.is_empty() のように書きます。詳しくはよく使う文字列メソッドを見てください。
Vec の len。 scores.len だけは例外で、E0616「field len of struct Vec is private」(構造体 Vec のフィールド len は非公開)が出ます。Vec には本当に len という隠れたフィールドがあるからです。それでも rustc は help: a method `len` also exists, call it with parentheses と付け加えてくれます。直し方:scores.len()。(scores.is_empty なら、ふつうの E0615 になります。)詳しくはベクタを見てください。
何かを変更するメソッド。 counter.increment; と1行だけ書いても E0615 になります。値がほしいわけではなくても、() がなければ何も実行されないからです。直し方:counter.increment();。
逆のパターン:フィールドをメソッドのように呼ぶ。 name がフィールドなのに user.name() と書くと、E0599 が出て、field, not a method(メソッドではなくフィールド)というラベルと help: remove the arguments(引数を取り除いてください)が表示されます。直し方:user.name。また、on_click: Box<dyn Fn() -> String> のようにフィールドにクロージャが入っているときは、rustc は surround the field access with parentheses(フィールドへのアクセスをかっこで囲んでください)と言います。呼ぶには (button.on_click)() と書きます。
関連するエラー
Section titled “関連するエラー”- E0609: no field
xon typeT:ドットのあとの名前が、その型のフィールドでもメソッドでもない。たいていはタイプミスか、取り出す必要のあるOptionです。 - E0599: no method named x found:逆向きのエラーです。
nameがフィールドなのにuser.name()と書いた、または存在しないメソッドを呼んだ。 - E0616: field is private:
Vecでscores.lenと書いた、または同じ名前の公開ゲッターがある非公開フィールドを読もうとした。代わりにメソッドを呼びましょう。 - E0061: wrong number of arguments:
()を付けたけれど、そのメソッドには引数が必要だった。 - E0610: primitive type doesn’t have fields:数値、
bool、charに.somethingと書いた。 - コンパイルエラーの一覧:エラー解説ページの全リストです。
理解度チェック
Section titled “理解度チェック”理解度チェック
1. 「attempted to take value of method is_empty on type String」と出ました。どう直しますか?
2. ふつう、しまっておくフィールドではなくメソッドにするべきなのはどれですか?
3. すべての単語の長さがほしいとき、words.iter().map(???) にはどれが使えますか?
スコア: 0 / 3
さらに詳しく
Section titled “さらに詳しく”- E0615 — Rust error codes index(英語):このエラーの公式の説明です。
- メソッド記法 — The Rust Programming Language 日本語版:メソッドの定義のしかたと呼び方、そしてフィールドと同じ名前を持つゲッターを説明している章です。
- 構造体を定義し、インスタンス化する — The Rust Programming Language 日本語版:フィールドと、それをドット記法で読む方法です。
- Method call expressions — The Rust Reference(英語):
value.method()の正確なルールです。 - メソッド — Rust By Example 日本語版:メソッドと関連関数の、実行できる短い例です。