Rust-Fehler E0401: can't use generic parameters from outer item (innere fn)
E0401 bedeutet: Eine Funktion, die in einer anderen Funktion steht, will den generischen Typ der äußeren benutzen, etwa T, oder das Self des impl, in dem sie sitzt. Es fühlt sich an, als müsste das gehen, schließlich steht die innere Funktion direkt in der äußeren. Aber in Rust ist ein verschachteltes fn ein eigenes Element, das das äußere T nicht sieht. Das überrascht oft, wenn du eine generische Funktion aufräumst und eine Hilfsfunktion in sie hineinziehst. Behoben ist es in einer Minute. Die Seite gehört zur Reihe Rust-Compilerfehler einfach erklärt.
Die Fehlermeldung
Abschnitt betitelt „Die Fehlermeldung“error[E0401]: can't use generic parameters from outer item --> src/main.rs:2:20 |1 | fn print_all<T: std::fmt::Display>(items: &[T]) { | - type parameter from outer item2 | fn show(item: &T) { // ❌ inneres `fn` sieht das äußere `T` nicht | ---- ^ use of generic parameter from outer item | | | generic parameter used in this inner function | = note: nested items are independent from their parent item for everything except for privacy and name resolutionhelp: try introducing a local generic parameter here |2 | fn show<T>(item: &T) { // ❌ inneres `fn` sieht das äußere `T` nicht | +++Auf Deutsch: „Generische Parameter des äußeren Elements können nicht benutzt werden.“ Die Hilfe sagt: „Führe hier einen eigenen generischen Parameter ein.“
Dieses Programm erzeugt den Fehler:
fn print_all<T: std::fmt::Display>(items: &[T]) { fn show(item: &T) { // ❌ inneres `fn` sieht das äußere `T` nicht println!("- {}", item); }
for item in items { show(item); }}
fn main() { print_all(&["apples", "pears"]);}Was sie bedeutet
Abschnitt betitelt „Was sie bedeutet“rustc zeigt auf drei Stellen:
- Das
-unterTin Zeile 1: „type parameter from outer item“ (Typparameter des äußeren Elements). Das ist dasT, das zuprint_allgehört. - Das
^unterTin Zeile 2: „use of generic parameter from outer item“ (Benutzung des generischen Parameters des äußeren Elements). Die innere Funktionshowversucht, diesesTzu benutzen. - Die
----untershow: „generic parameter used in this inner function“ (generischer Parameter in dieser inneren Funktion benutzt). Das ist die Funktion, die das nicht darf.
Das note: fasst die Regel in einer Zeile zusammen: Verschachtelte Elemente sind von ihrem Elternelement unabhängig, außer bei Sichtbarkeit und Namensauflösung. Steht fn show in print_all, versteckt das nur seinen Namen vor dem Rest der Datei. Es gibt show keinen Zugriff auf irgendetwas, das zu print_all gehört: nicht auf sein T, nicht auf seine Parameter, nicht auf seine lokalen Variablen.
Das ist wie ein Gästezimmer in einer WG. Das Zimmer liegt in der Wohnung, aber der Gast bekommt nicht den Schlüssel zu deinem Schlafzimmer, nur weil er am selben Flur schläft.
Warum Rust das macht
Abschnitt betitelt „Warum Rust das macht“Eine generische Funktion wie print_all<T> ist eine Vorlage. Rust erzeugt für jedes T, mit dem du sie aufrufst, eine eigene Kopie: eine für &str, eine für i32 und so weiter. Ein verschachteltes fn dagegen wird einmal übersetzt, als eigenes Element, genau so, als hättest du es ganz oben in die Datei geschrieben. In show gibt es also kein einzelnes T, das gemeint sein könnte.
Rust hätte verschachtelte Funktionen mit ihrem Elternelement mitkopieren können. Dann würden sie sich aber ganz anders verhalten als jedes andere fn. Stattdessen ist die Regel einfach: Ein inneres fn kennt nur, was du ihm gibst, über seine eigenen generischen Parameter und Argumente. Willst du etwas, das den umgebenden Code doch sieht, hat Rust dafür Closures. Siehe Generics und Closures.
So behebst du den Fehler
Abschnitt betitelt „So behebst du den Fehler“Lösung 1: Der inneren Funktion einen eigenen generischen Parameter geben
Abschnitt betitelt „Lösung 1: Der inneren Funktion einen eigenen generischen Parameter geben“Das ist meistens die richtige Wahl. Deklariere <T> auch an der inneren Funktion, mit denselben Bounds, die sie braucht. Die Hilfe von rustc schlägt fn show<T> vor. Schreibst du aber nur <T>, bekommst du einen neuen Fehler, E0277 (`T` doesn't implement `std::fmt::Display`), weil das neue T noch keine Bounds hat. Übernimm die Bounds:
use std::fmt::Display;
fn print_all<T: Display>(items: &[T]) { fn show<T: Display>(item: &T) { println!("- {}", item); }
for item in items { show(item); }}
fn main() { print_all(&["Äpfel", "Birnen"]); print_all(&[3, 7]);}Ausgabe:
- Äpfel- Birnen- 3- 7Das innere T ist ein neuer Parameter, der zufällig genauso heißt. Ruft print_all dann show(item) auf, setzt Rust dafür das äußere T ein. Du könntest ihn auch U nennen, damit das deutlicher wird.
Lösung 2: Stattdessen ein Closure benutzen
Abschnitt betitelt „Lösung 2: Stattdessen ein Closure benutzen“Nimm diese Lösung, wenn die Hilfsfunktion klein ist oder auch die Variablen der äußeren Funktion braucht. Ein Closure ist kein eigenes Element, also sieht es alles um sich herum: das äußere T, die Parameter der Funktion und ihre lokalen Variablen. Hier benutzt das Closure außerdem label, an das ein verschachteltes fn nie herankäme:
use std::fmt::Display;
fn print_all<T: Display>(label: &str, items: &[T]) { let show = |item: &T| println!("{}: {}", label, item);
for item in items { show(item); }}
fn main() { print_all("Obst", &["Äpfel", "Birnen"]);}Ausgabe:
Obst: ÄpfelObst: BirnenClosures können nicht selbst generisch sein, müssen es hier aber auch nicht: In print_all<T> steht &T schon für einen konkreten Typ pro Kopie der Funktion. Siehe Closures.
Lösung 3: Die Funktion nach außen ziehen (oder ins impl)
Abschnitt betitelt „Lösung 3: Die Funktion nach außen ziehen (oder ins impl)“Nimm diese Lösung, wenn die Hilfsfunktion auch für sich nützlich ist oder wenn der Fehler von Self handelt. In einer Methode kann ein verschachteltes fn auch Self nicht benutzen, weil es auf dieselbe Weise außerhalb des impl steht:
error[E0401]: can't use `Self` from outer item --> src/main.rs:7:24 |5 | impl Inventory { | ---- `Self` type implicitly declared here, by this `impl`6 | fn report(&self) {7 | fn count(inv: &Self) -> usize { // ❌ inneres `fn` sieht `Self` nicht | ----- ^^^^ use of `Self` from outer item | | | `Self` used in this inner function | = note: nested items are independent from their parent item for everything except for privacy and name resolutionhelp: refer to the type directly here instead |7 - fn count(inv: &Self) -> usize { // ❌ inneres `fn` sieht `Self` nicht7 + fn count(inv: &Inventory) -> usize { // ❌ inneres `fn` sieht `Self` nicht |Hier sagt rustc: „Self des äußeren Elements kann nicht benutzt werden“, und die Hilfe: „Nenne hier stattdessen direkt den Typ.“
Das fehlerhafte Programm:
struct Inventory { items: Vec<String>,}
impl Inventory { fn report(&self) { fn count(inv: &Self) -> usize { // ❌ inneres `fn` sieht `Self` nicht inv.items.len() } println!("{} items", count(self)); }}
fn main() { let inv = Inventory { items: vec!["rope".to_string()] }; inv.report();}Die Hilfe von rustc (schreib &Inventory) funktioniert. Meist ist es aber sauberer, count eine Ebene nach oben zu ziehen, sodass es eine eigene Methode wird. Dort funktionieren Self und self wie gewohnt:
struct Inventory { items: Vec<String>,}
impl Inventory { fn count(&self) -> usize { self.items.len() }
fn report(&self) { println!("{} Gegenstände", self.count()); }}
fn main() { let inv = Inventory { items: vec!["Seil".to_string(), "Lampe".to_string()], }; inv.report();}Ausgabe:
2 GegenständeBei einer generischen Hilfsfunktion wie show heißt „nach außen ziehen“: ein fn show<T: Display>(item: &T) auf oberster Ebene, das auch andere Funktionen wiederverwenden können. Siehe Structs und Funktionen.
Wo du ihn oft siehst
Abschnitt betitelt „Wo du ihn oft siehst“Ein Struct oder Enum in einer generischen Funktion. fn group<T>(items: Vec<T>) mit struct Pair { a: T, b: T } darin ergibt E0401 bei jedem T. Lösung: Mach auch den inneren Typ generisch, struct Pair<T> { a: T, b: T }, oder zieh ihn auf die oberste Ebene.
Eine const, die von T abhängt. const SIZE: usize = std::mem::size_of::<T>(); in einer generischen Funktion ist ebenfalls E0401, denn auch eine const ist ein Element. Lösung: Nimm ein let: let size = std::mem::size_of::<T>();.
Eine innere Funktion, die eine lokale Variable benutzt. Das ist dieselbe Idee, nur mit einem Wert statt einem Typ, aber mit anderem Code: E0434 (can't capture dynamic environment in a fn item, ein fn-Element kann seine Umgebung nicht einfangen). Lösung: Übergib den Wert als Argument, oder nimm ein Closure (Lösung 2).
Verwandte Fehler
Abschnitt betitelt „Verwandte Fehler“- E0411:
Selfaußerhalb jedesimploder Traits benutzt, etwa in einemfn new() -> Selfauf oberster Ebene. - E0277: Der Trait Bound ist nicht erfüllt. Das bekommst du nach
fn show<T>, wenn duT: Displaynicht übernimmst. - E0425: Ein Wert oder Typ wird nicht gefunden, etwa ein Typ, den du nie deklariert hast.
- E0107: falsche Anzahl generischer Argumente, ein anderer Fehler mit Generics.
- E0434: Ein inneres
fnbenutzt eine lokale Variable der äußeren Funktion, siehe oben. - Alle Compilerfehler: die vollständige Liste der Fehlerseiten.
Kurzer Check
Abschnitt betitelt „Kurzer Check“Kurzer Check
1. Warum kann ein verschachteltes fn show(item: &T) in fn print_all<T>(...) das T nicht benutzen?
2. Welche Hilfsfunktion in fn print_all<T: Display> kann das äußere T und die äußeren lokalen Variablen benutzen?
3. Du folgst der Hilfe von rustc, schreibst fn show<T>(item: &T) und gibst item mit {} aus. Was passiert?
Punkte: 0 / 3
Weiterlesen
Abschnitt betitelt „Weiterlesen“- E0401 – Rust error codes index (auf Englisch): die offizielle Erklärung, mit den drei Lösungen: ein eigener generischer Parameter, ein Closure oder die Funktion nach außen ziehen.
- Item declarations – The Rust Reference (auf Englisch): die Regel, dass ein inneres Element die generischen Parameter, Parameter und lokalen Variablen der äußeren Funktion nicht einfängt.
- Generische Datentypen – Das Rust-Buch: wie
<T>und Trait Bounds bei Funktionen, Structs und Methoden funktionieren. - Erfassen der Umgebung – Das Rust-Buch: wie Closures ihre Umgebung einfangen, was verschachtelte Funktionen nicht können.