Erreur Rust E0615 : attempted to take value of method (méthode sans parenthèses)
E0615 veut dire : vous avez écrit value.something sans parenthèses, mais something est une méthode, pas un champ. En Rust, une méthode ne s’exécute que si vous l’appelez avec (), même quand elle ne prend aucun argument. Cette erreur est très fréquente si vous venez de C#, de JavaScript ou de Python, où .Count, .length ou une propriété ressemblent à de simples champs. La correction tient presque toujours en deux caractères. (Cette page fait partie de Les erreurs du compilateur Rust expliquées.)
Le message d’erreur
Section intitulée « Le message d’erreur »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 | ++Le compilateur vous dit : « tentative de prendre la valeur de la méthode len sur le type String ». L’étiquette précise « method, not a field » (c’est une méthode, pas un champ), et l’aide propose : « use parentheses to call the method » (utilisez des parenthèses pour appeler la méthode).
Voici le programme qui la provoque :
fn main() { let name = String::from("Ferris"); println!("{} has {} letters", name, name.len); // ❌ `len` is a method}(Le commentaire dit : len est une méthode.)
Ce que ça veut dire
Section intitulée « Ce que ça veut dire »Lisez le titre en partant de la fin : on type String (sur le type String), c’est le type que Rust a examiné, et method len (la méthode len), c’est ce qu’il y a trouvé. Rust a donc bien trouvé len. Simplement, ce n’est pas une valeur que l’on peut lire comme un champ. C’est un morceau de code qui doit s’exécuter.
L’étiquette method, not a field et les ++ sous les () dans l’aide disent exactement la même chose : ajoutez les parenthèses, et c’est réglé.
Pensez à un distributeur automatique. Un champ, c’est une étiquette imprimée sur la machine (« Machine n° 12 ») : vous la lisez, c’est tout. Une méthode, c’est un bouton (« Rendre la monnaie ») : rien ne se passe tant que vous n’appuyez pas dessus. name.len montre le bouton du doigt. name.len() appuie dessus.
Pourquoi Rust fait ça
Section intitulée « Pourquoi Rust fait ça »Rust sépare strictement les champs et les méthodes. Un champ, ce sont des données rangées à l’intérieur de la struct. Une méthode, c’est une fonction définie dans un bloc impl qui prend self. On lit un champ avec value.field et on appelle une méthode avec value.method(). Voir Ajouter du comportement avec des méthodes.
Beaucoup de langages brouillent cette frontière avec des propriétés : list.Count en C# ou text.length en JavaScript ressemblent à des champs, mais exécutent du code en coulisses. Rust n’a volontairement pas de propriétés. Quand vous voyez (), vous savez que du code s’exécute et peut prendre du temps ; quand vous ne les voyez pas, vous savez que c’est une simple lecture de données stockées. Rien n’est caché.
Une struct peut même avoir un champ et une méthode du même nom, par exemple un champ privé balance et une méthode publique balance(). C’est possible justement parce que Rust ne devine jamais ce que vous vouliez dire : sans parenthèses, c’est le champ ; avec des parenthèses, c’est la méthode.
Comment corriger
Section intitulée « Comment corriger »Solution 1 : ajouter les parenthèses
Section intitulée « Solution 1 : ajouter les parenthèses »Choisissez-la presque à chaque fois. Suivez la ligne help: et appelez la méthode. Ça marche de la même façon pour vos propres méthodes (comme total sur un panier) et pour celles de la bibliothèque standard comme len, is_empty, trim et count :
struct Item { price: u32, // en centimes 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!("{} a {} lettres", name, name.len()); // ✅ appelée
let cart = Cart { items: vec![Item { price: 250, qty: 2 }, Item { price: 100, qty: 1 }], }; if !cart.items.is_empty() { println!("Total : {} centimes", cart.total()); // ✅ appelée }}Sortie :
Ferris a 6 lettresTotal : 600 centimesSi la méthode a besoin d’arguments, rustc vous montre où ils vont avec un _ qui leur réserve la place. Pour s.contains, l’aide affiche s.contains(_) : remplissez-le avec ce que vous cherchez, par exemple s.contains("world"). Si vous ne savez pas ce qu’attend une méthode, l’erreur suivante de rustc (E0061) vous dira combien d’arguments elle prend.
Solution 2 : passer la méthode comme une fonction, pas value.method
Section intitulée « Solution 2 : passer la méthode comme une fonction, pas value.method »Choisissez-la quand vous ne vouliez pas appeler la méthode tout de suite, mais la confier à autre chose, comme .map() ou sort_by_key. Écrire .map(words[0].len) donne aussi E0615. Vous avez deux bonnes options : une petite closure (fonction anonyme) qui appelle la méthode, ou le chemin complet de la méthode, String::len, qui s’utilise comme un nom de fonction :
fn main() { let words = vec![String::from("crab"), String::from("ferris"), String::from("rust")];
// une closure qui appelle la méthode sur chaque élément let lengths: Vec<usize> = words.iter().map(|w| w.len()).collect(); println!("{:?}", lengths);
// le chemin de la méthode s'utilise comme un nom de fonction 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);}Sortie :
[4, 6, 4][4, 6, 4]["crab", "rust", "ferris"](Les mots restent en anglais ici, parce que le programme affiche leur longueur : une lettre accentuée compterait pour 2 octets.)
String::len fonctionne ici parce que iter() vous donne des éléments &String, et que String::len prend justement un &String. Quand les types ne correspondent pas exactement, la closure |w| w.len() est le choix facile qui marche toujours.
Solution 3 : en faire un champ, si ce sont vraiment des données stockées
Section intitulée « Solution 3 : en faire un champ, si ce sont vraiment des données stockées »Choisissez-la quand l’erreur est dans votre propre struct et que vous vous rendez compte que la valeur devrait simplement être stockée. C’est une question de conception : champ ou méthode ?
- Faites-en un champ quand la valeur est donnée une fois pour toutes et ne dépend de rien d’autre : un
id, unname, une datecreated_at. - Faites-en une méthode quand la valeur est calculée à partir d’autres données : un
totalà partir des articles, uneareaà partir de la largeur et de la hauteur, unelenà partir du contenu.
Un champ total stocké deviendrait faux dès que quelqu’un ajoute un article et oublie de le mettre à jour. Une méthode total() est recalculée à chaque fois, elle ne peut donc jamais se tromper. Ici, id est un champ et total une méthode, et vous voyez que la méthode reste juste quand la commande change :
mod shop { pub struct Order { pub id: u32, // stocké : fixé une fois, lu avec order.id prices: Vec<u32>, // privé : modifié seulement par les méthodes }
impl Order { pub fn new(id: u32) -> Self { Order { id, prices: Vec::new() } }
pub fn add(&mut self, price: u32) { self.prices.push(price); }
// calculé : toujours d'accord avec les prix, jamais périmé 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!("Commande {} : {} articles, {} centimes", order.id, order.item_count(), order.total());
order.add(50); println!("Commande {} : {} articles, {} centimes", order.id, order.item_count(), order.total());}Sortie :
Commande 17 : 2 articles, 350 centimesCommande 17 : 3 articles, 400 centimesIl existe une voie intermédiaire, que vous verrez dans beaucoup de bibliothèques : un champ privé plus une méthode publique du même nom (un « getter », ou accesseur). Le reste du code peut lire la valeur avec acc.balance(), mais ne peut pas la modifier directement. Écrire acc.balance depuis l’extérieur du module donne alors une erreur un peu différente, E0616 « field balance of struct Account is private » (le champ balance de Account est privé), avec l’indice a method `balance` also exists, call it with parentheses (une méthode balance existe aussi, appelez-la avec des parenthèses). Même correction : ajoutez (). Voir Tout est privé par défaut.
Où vous le verrez souvent
Section intitulée « Où vous le verrez souvent »Les habitudes d’autres langages. name.len, list.is_empty, text.trim, words.iter().count donnent tous E0615. Ce sont toutes des méthodes en Rust, elles ont donc toutes besoin de (). Correction : name.len(), list.is_empty(), et ainsi de suite. Voir Méthodes utiles sur les chaînes.
len sur un Vec. scores.len est la seule exception au message : vous obtenez E0616, « field len of struct Vec is private » (le champ len de Vec est privé), parce que Vec a vraiment un champ caché appelé len. rustc ajoute quand même help: a method `len` also exists, call it with parentheses. Correction : scores.len(). (scores.is_empty donne l’E0615 habituelle.) Voir Les vecteurs.
Une méthode qui modifie quelque chose. counter.increment; seul sur sa ligne donne aussi E0615, même si vous ne vouliez pas de valeur. Sans (), rien ne s’exécute. Correction : counter.increment();.
L’inverse : appeler un champ comme une méthode. Si name est un champ, user.name() donne E0599 avec l’étiquette field, not a method (un champ, pas une méthode) et help: remove the arguments (retirez les arguments). Correction : user.name. Et si le champ contient une closure, comme on_click: Box<dyn Fn() -> String>, rustc dit surround the field access with parentheses (entourez l’accès au champ de parenthèses) : écrivez (button.on_click)() pour l’appeler.
Erreurs liées
Section intitulée « Erreurs liées »- E0609 : no field
xon typeT: le nom après le point n’est ni un champ ni une méthode de ce type. En général une faute de frappe, ou uneOptionqu’il faut d’abord ouvrir. - E0599 : no method named x found : le sens inverse,
user.name()alors quenameest un champ, ou une méthode qui n’existe pas. - E0616 : field is private :
scores.lensur unVec, ou un champ privé avec un getter public du même nom. Appelez plutôt la méthode. - E0061 : wrong number of arguments : vous avez ajouté les
(), mais la méthode a besoin d’arguments. - E0610 : primitive type doesn’t have fields :
.quelque_chosesur un nombre, unboolou unchar. - Toutes les erreurs du compilateur expliquées
Petit quiz
Section intitulée « Petit quiz »Petit quiz
1. Vous obtenez « attempted to take value of method is_empty on type String ». Quelle est la correction ?
2. Laquelle de ces valeurs devrait en général être une méthode plutôt qu'un champ stocké ?
3. Vous voulez la longueur de chaque mot : words.iter().map(???). Qu'est-ce qui marche ?
Score : 0 / 3
Pour aller plus loin
Section intitulée « Pour aller plus loin »- E0615 — Rust error codes index (en anglais) : l’explication officielle de cette erreur.
- Method Syntax — The Rust Book (en anglais) : comment on définit et appelle les méthodes, et les getters qui portent le même nom qu’un champ.
- Defining and Instantiating Structs — The Rust Book (en anglais) : les champs, et comment les lire avec la notation pointée.
- Method call expressions — The Rust Reference (en anglais) : les règles exactes de
value.method(). - Methods — Rust by Example (en anglais) : de courts exemples exécutables de méthodes et de fonctions associées.