Zum Inhalt springen

Rust-Fehler E0061: this function takes N arguments but M were supplied

E0061 bedeutet: Du hast eine Funktion mit der falschen Anzahl von Argumenten aufgerufen – mit zu wenigen oder zu vielen. Das ist einer der freundlichsten Fehler in Rust, denn rustc sagt dir genau, welches Argument fehlt oder zu viel ist und welchen Typ es haben sollte. Er taucht oft bei Leuten auf, die von Python, JavaScript oder C# kommen. Dort sind Standardargumente und Überladen ganz normal. Rust hat beides nicht, und diese Seite zeigt dir, was du stattdessen machst. Die anderen Fehler dieser Reihe findest du unter Rust-Compilerfehler.

error[E0061]: this function takes 2 arguments but 1 argument was supplied
--> src/main.rs:6:5
|
6 | greet("Ferris"); // ❌ greet will zwei Argumente
| ^^^^^---------- argument #2 of type `&str` is missing
|
note: function defined here
--> src/main.rs:1:4
|
1 | fn greet(name: &str, greeting: &str) {
| ^^^^^ --------------
help: provide the argument
|
6 | greet("Ferris", /* &str */); // ❌ greet will zwei Argumente
| ++++++++++++

Auf Deutsch heißt das ungefähr: „Diese Funktion nimmt 2 Argumente, aber es wurde 1 Argument übergeben. Argument Nr. 2 vom Typ &str fehlt.“ Dieses Programm erzeugt die Meldung:

fn greet(name: &str, greeting: &str) {
println!("{greeting}, {name}!");
}
fn main() {
greet("Ferris"); // ❌ greet will zwei Argumente
}

Zu viele Argumente führen zum selben Code, mit einem Label „unexpected argument“ (unerwartetes Argument) und dem Hinweis „remove the extra argument“ (entferne das überzählige Argument):

error[E0061]: this function takes 2 arguments but 3 arguments were supplied
--> src/main.rs:6:13
|
6 | let a = area(3, 4, 5);
| ^^^^ - unexpected argument #3 of type `{integer}`

Bei einer Methode, die du mit Punkt aufrufst, steht in der ersten Zeile stattdessen „this method takes…“. Bei einem Tupel-Struct wie Rgb(255, 0) heißt es „this struct takes 3 arguments“. Es ist jedes Mal derselbe Fehler.

Die Meldung hat drei Teile, und jeder hilft dir:

  1. Die erste Zeile zählt: Die Funktion will 2 Argumente, du hast 1 übergeben.
  2. Das Label unter deinem Aufruf benennt das Problem: „argument #2 of type &str is missing“ (Argument Nr. 2 vom Typ &str fehlt).
  3. Die note: springt zur Definition der Funktion und unterstreicht den Parameter, den du vergessen hast: greeting: &str.

Die Zeile help: zeigt, wo das Argument hingehört. /* &str */ ist nur ein Platzhalter, der dir den Typ verrät. Es ist ein Kommentar, also kompiliert es nicht, wenn du es einfach so übernimmst. Setz dort einen echten Wert ein, etwa "Hallo".

Das ist wie ein Kaffeeautomat, der einen Becher und eine Münze braucht. Wenn du nur die Münze einwirfst, bekommst du keinen halben Kaffee. Er startet einfach nicht.

In Rust ist die Signatur einer Funktion ein genauer Vertrag: diese Parameter, in dieser Reihenfolge, mit diesen Typen. Jeder Aufruf muss dazu passen. Es gibt keine Standardargumente (greeting = "Hallo"), kein Überladen (zwei Funktionen namens greet mit verschiedenen Parametern) und keine Funktionen, die „beliebig viele“ Argumente nehmen. Versuchst du, einen Standardwert zu schreiben, bekommst du error: parameter defaults are not supported (Standardwerte für Parameter werden nicht unterstützt). Ein zweites greet führt zu E0428, „the name greet is defined multiple times“ (der Name greet ist mehrfach definiert).

Das klingt streng, zahlt sich aber aus. Wenn du greet(name, greeting) liest, weißt du genau, welche Funktion läuft und was sie bekommt. Wenn du einer Funktion einen Parameter hinzufügst, listet der Compiler jeden Aufruf auf, der angepasst werden muss. So vergisst du keinen. (Dinge wie println!, die tatsächlich beliebig viele Werte nehmen, sind Makros – dafür steht das !. Siehe Makros.)

Wie Parameter funktionieren, erklärt die Seite Funktionen.

Lösung 1: Genau das übergeben, was die Signatur verlangt

Abschnitt betitelt „Lösung 1: Genau das übergeben, was die Signatur verlangt“

Nimm diese Lösung fast immer. Öffne die Stelle aus der note:, lies die Parameterliste und übergib einen Wert pro Parameter, in der richtigen Reihenfolge:

fn greet(name: &str, greeting: &str) {
println!("{greeting}, {name}!");
}
fn main() {
greet("Ferris", "Hallo");
greet("Corro", "Guten Morgen");
}

Ausgabe:

Hallo, Ferris!
Guten Morgen, Corro!
▶ Selbst ausprobieren

Hast du gerade die Funktion geändert, und jetzt ist jeder Aufruf kaputt? Dann hilft dir der Compiler: Korrigiere jeden Aufruf, den er auflistet, oder frag dich, ob der neue Parameter nicht eigentlich optional sein sollte (Lösung 3).

Nimm diese Lösung, wenn die Funktion einen self-Parameter hat. Eine Methode rufst du normalerweise als value.method(args) auf. Der Wert vor dem Punkt füllt self, also zählst du ihn nicht mit. Schreibst du stattdessen Type::method(args), füllt nichts das self:

error[E0061]: this function takes 2 arguments but 1 argument was supplied
--> src/main.rs:13:5
|
13 | Account::deposit(50);
| ^^^^^^^^^^^^^^^^ -- argument #1 of type `&mut Account` is missing
|
help: provide the argument
|
13 | Account::deposit(/* &mut Account */, 50);
| +++++++++++++++++++

Achte aufs Zählen. Beim Aufruf als Account::deposit(...) sagt rustc, die Funktion nimmt 2 Argumente, weil &mut self mitzählt. Beim Aufruf als acc.deposit() sagt es, die Methode nimmt 1 Argument, weil der Punkt self schon liefert. Beide Formen funktionieren, sobald alle Argumente da sind:

struct Account {
balance: i64,
}
impl Account {
fn deposit(&mut self, amount: i64) {
self.balance += amount;
}
}
fn main() {
let mut acc = Account { balance: 0 };
acc.deposit(50); // der übliche Weg
Account::deposit(&mut acc, 25); // derselbe Aufruf, ausgeschrieben
println!("Kontostand: {}", acc.balance);
}

Ausgabe:

Kontostand: 75
▶ Selbst ausprobieren

Im Alltag nimmst du die Punkt-Form. Type::method(&x, a) ist praktisch, wenn du die Methode selbst irgendwohin übergeben willst, etwa team.iter().map(Player::label) (Beispiel). Hat eine Funktion überhaupt kein self (wie new), kannst du sie nur als Type::function(...) aufrufen. Siehe Structs und E0424.

Lösung 3: Option<T> für ein Argument, das manchmal nicht gebraucht wird

Abschnitt betitelt „Lösung 3: Option<T> für ein Argument, das manchmal nicht gebraucht wird“

Nimm diese Lösung, wenn ein Argument meistens einen sinnvollen Standardwert hat. Mach es zu einer Option, übergib None für „nimm den Standardwert“ und setz ihn mit unwrap_or ein:

fn connect(host: &str, port: u16, timeout_secs: Option<u64>) {
let timeout = timeout_secs.unwrap_or(30);
println!("Verbinde mit {host}:{port} (Timeout {timeout} s)");
}
fn main() {
connect("localhost", 8080, None);
connect("example.com", 443, Some(5));
}

Ausgabe:

Verbinde mit localhost:8080 (Timeout 30 s)
Verbinde mit example.com:443 (Timeout 5 s)
▶ Selbst ausprobieren

Ehrlich gesagt: Die Aufrufer schreiben das Argument trotzdem hin, nämlich None. Rust mag das so, weil man dem Aufruf ansieht, dass ein Standardwert benutzt wird. Willst du es gar nicht schreiben, mach stattdessen zwei Funktionen mit klaren Namen, etwa connect(host, port), das connect_with_timeout(host, port, 30) aufruft. Das ist die übliche Rust-Antwort auf Überladen. Siehe Option.

Nimm diese Lösung, wenn eine Funktion viele Einstellungen hat und die meisten Aufrufer nur ein oder zwei davon ändern. Pack die Einstellungen in ein Struct, implementiere Default und lass die Aufrufer mit ..Default::default() nur das ausfüllen, was sie brauchen:

struct ServerConfig {
host: String,
port: u16,
timeout_secs: u64,
verbose: bool,
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
host: String::from("localhost"),
port: 8080,
timeout_secs: 30,
verbose: false,
}
}
}
fn start(config: ServerConfig) {
println!(
"Lausche auf {}:{} (Timeout {} s, verbose: {})",
config.host, config.port, config.timeout_secs, config.verbose
);
}
fn main() {
start(ServerConfig::default());
start(ServerConfig { port: 3000, verbose: true, ..Default::default() });
}

Ausgabe:

Lausche auf localhost:8080 (Timeout 30 s, verbose: false)
Lausche auf localhost:3000 (Timeout 30 s, verbose: true)
▶ Selbst ausprobieren

So bekommst du auch so etwas wie benannte Argumente: port: 3000 sagt, was die Zahl bedeutet. Ist der Standardwert jedes Felds einfach der Standardwert seines Typs (0, false, leerer String), schreibt #[derive(Default)] das impl für dich. Bei größeren APIs ist das Builder-Pattern (Server::builder().port(3000).build()) der nächste Schritt. Siehe Structs und Entwurfsmuster.

Nachdem du einen Parameter hinzugefügt hast. Du gibst fn save(path: &str) einen zweiten Parameter, und jeder alte Aufruf save("notes.txt") bricht mit E0061. Lösung: Pass jeden Aufruf an, den der Compiler auflistet, oder nimm Lösung 3 oder 4, wenn der neue Wert meistens gleich ist.

Methoden der Standardbibliothek. names.push("Cy", "Di") führt zu „this method takes 1 argument but 2 arguments were supplied“. push fügt genau ein Element hinzu. Lösung: Ruf es zweimal auf, oder nimm names.extend(["Cy", "Di"]). Siehe Vektoren.

Tupel-Structs. struct Rgb(u8, u8, u8); und dann Rgb(255, 0) führt zu „this struct takes 3 arguments but 2 arguments were supplied“. Lösung: Gib jedes Feld an, Rgb(255, 0, 0).

Gewohnheiten aus anderen Sprachen. Pythons greet("Ferris", greeting="Hi") ist keine Rust-Syntax. greet(greeting = "Hi") benennt kein Argument. rustc liest es als Zuweisung an eine Variable namens greeting und meldet zusätzlich E0425. Lösung: Übergib Werte nach ihrer Position, oder nimm ein Config-Struct (Lösung 4).

Closures. Rufst du add(1) auf, wenn add das Closure |a: i32, b: i32| a + b ist, bekommst du dieselbe Meldung, aber unter dem Code E0057 (der Closure-Variante). Die Lösung ist dieselbe: Übergib beide Werte.

  • E0308: Die Anzahl der Argumente stimmt, aber eins hat den falschen Typ – oft, weil zwei vertauscht wurden.
  • E0424: self in einer Funktion ohne self-Parameter benutzt – die andere Seite von Methode und assoziierter Funktion.
  • E0599: Es gibt keine Methode mit diesem Namen, zum Beispiel wenn du eine assoziierte Funktion wie new mit Punkt aufrufst.
  • E0425: Ein Wert wird nicht gefunden. Das kann neben E0061 auftauchen, wenn du benannte Argumente versuchst.
  • Alle Compilerfehler: die vollständige Liste der Fehlerseiten. Ebenfalls verwandt, aber ohne eigene Seite: E0428 (eine Funktion doppelt definiert, beim Versuch zu überladen). E0057 (Closure mit der falschen Anzahl von Argumenten aufgerufen) hat eine eigene Seite.

Kurzer Check

1. fn area(w: u32, h: u32) -> u32 wird als area(5) aufgerufen. Was meldet rustc?

2. Eine Methode ist fn deposit(&mut self, amount: i64). Welcher Aufruf ist richtig?

3. Du willst ein timeout-Argument, das die meisten Aufrufer nicht interessiert. Was ist ein üblicher Weg in Rust?

Punkte: 0 / 3