Перейти к содержимому

Ошибка Rust E0615: attempted to take value of method (метод без скобок)

E0615 значит: ты написал value.something без скобок, но something — это метод, а не поле. В Rust метод выполняется только тогда, когда ты вызываешь его с (), даже если аргументов у него нет. Эта ошибка особенно часто встречается у тех, кто пришёл из C#, JavaScript или Python, где .Count, .length или свойство выглядят как обычные поля. Исправление почти всегда занимает два символа. (Эта страница входит в серию Ошибки компилятора Rust с объяснениями.)

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` is a method
| ^^^ method, not a field
|
help: use parentheses to call the method
|
3 | println!("{} has {} letters", name, name.len()); // ❌ `len` is a method
| ++

Компилятор сообщает: ты попытался взять значение метода len у типа String, а это метод, а не поле, — и предлагает добавить скобки. Вот программа, которая выдаёт эту ошибку:

fn main() {
let name = String::from("Ferris");
println!("{} has {} letters", name, name.len); // ❌ `len` is a method
}

Читай заголовок с конца: on type String — это тип, на который посмотрел Rust, а method len — то, что он там нашёл. То есть Rust нашёл len. Просто это не значение, которое можно прочитать, как поле. Это кусок кода, который нужно выполнить.

Пометка method, not a field («метод, а не поле») и ++ под () в подсказке говорят ровно это: добавь скобки — и готово.

Представь торговый автомат. Поле — это надпись на автомате («Автомат №12»): ты её просто читаешь. Метод — это кнопка («Выдать сдачу»): ничего не происходит, пока ты её не нажмёшь. name.len показывает на кнопку. name.len() нажимает её.

Rust строго разделяет поля и методы. Поле — это данные, хранящиеся внутри структуры. Метод — это функция из блока impl, которая принимает self. Поле читают через value.field, метод вызывают через value.method(). Подробнее — в разделе Добавляем поведение: методы.

Многие языки размывают эту границу свойствами (properties): list.Count в C# или text.length в JavaScript выглядят как поля, но за кулисами выполняют код. В Rust свойств нет — намеренно. Видишь () — значит, выполняется код, и это может занять время. Не видишь — значит, это простое чтение хранимых данных. Ничего не спрятано.

У структуры даже могут быть поле и метод с одинаковым именем, например приватное поле balance и публичный метод balance(). Это возможно только потому, что Rust никогда не гадает, что ты имел в виду: без скобок — поле, со скобками — метод.

Это решение подходит почти всегда. Следуй строке 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!("В имени {} {} букв", 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 6 букв
Итого: 600 центов
▶ Попробуй сам

Имя "Ferris" осталось латиницей: len() считает байты, а каждая кириллическая буква занимает 2 байта, так что для «Феррис» получилось бы 12.

Если методу нужны аргументы, rustc показывает, куда их вписать, с заглушкой _. Для s.contains подсказка выглядит как s.contains(_): впиши то, что ищешь, например s.contains("world"). Если не уверен, что нужно методу, следующая ошибка rustc (E0061) скажет, сколько аргументов он принимает.

Исправление 2: передай метод как функцию, а не value.method

Заголовок раздела «Исправление 2: передай метод как функцию, а не value.method»

Выбирай этот вариант, когда ты не хотел вызывать метод прямо сейчас, а хотел передать его куда-то ещё, например в .map() или sort_by_key. .map(words[0].len) тоже даёт E0615. Есть два хороших варианта: короткое замыкание, которое вызывает метод, или полный путь метода, 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"]
▶ Попробуй сам

Слова здесь на английском по той же причине: len() считает байты. String::len подходит, потому что iter() даёт элементы типа &String, а String::len как раз принимает &String. Когда типы не совпадают точно, замыкание |w| w.len() — простой выбор, который работает всегда.

Исправление 3: сделай это полем, если это правда хранимые данные

Заголовок раздела «Исправление 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);
}
// вычисляется: всегда совпадает с ценами, не может устареть
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 центов
▶ Попробуй сам

Есть и промежуточный путь, который встречается во многих библиотеках: приватное поле плюс публичный метод с тем же именем («геттер», getter). Другой код может прочитать значение через acc.balance(), но не может изменить его напрямую. Если написать acc.balance снаружи модуля, получишь немного другую ошибку — E0616 «field balance of struct Account is private» с подсказкой a method `balance` also exists, call it with parentheses. Исправление то же: добавь (). Смотри Всё по умолчанию приватное.

Привычки из других языков. name.len, list.is_empty, text.trim, words.iter().count — всё это даёт E0615. Каждое из них в Rust — метод, поэтому каждому нужны (). Исправление: name.len(), list.is_empty() и так далее. Смотри Полезные методы строк.

len у Vec. scores.len — единственное исключение из этого сообщения: ты получишь E0616, «field len of struct Vec is private», потому что у Vec действительно есть скрытое поле с именем len. rustc всё равно добавит help: a method `len` also exists, call it with parentheses. Исправление: scores.len(). (scores.is_empty даёт обычную E0615.) Смотри урок Векторы.

Метод, который что-то меняет. counter.increment; отдельной строкой тоже даёт 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)().

Проверь себя

1. Ты получил «attempted to take value of method is_empty on type String». Как исправить?

2. Что обычно лучше сделать методом, а не хранимым полем?

3. Тебе нужна длина каждого слова: words.iter().map(???). Что сработает?

Результат: 0 / 3