Rust 에러 E0061: this function takes N arguments but M were supplied (인자 개수)
E0061은 함수를 잘못된 개수의 인자로 호출했다는 뜻이에요. 너무 적게 넘겼거나 너무 많이 넘긴 거죠. Rust에서 가장 친절한 에러 중 하나예요. 어떤 인자가 빠졌는지 또는 남는지, 그리고 그 인자의 타입이 무엇이어야 하는지 rustc가 정확히 알려 주거든요. 기본 인자와 오버로딩이 당연한 Python, JavaScript, C#에서 온 분들이 특히 자주 만나요. Rust에는 둘 다 없어요. 이 페이지에서 그 대신 무엇을 하면 되는지 보여 드릴게요. 이 시리즈의 다른 에러는 Rust 컴파일러 에러에서 볼 수 있어요.
에러 메시지
섹션 제목: “에러 메시지”error[E0061]: this function takes 2 arguments but 1 argument was supplied --> src/main.rs:6:5 |6 | greet("Ferris"); // ❌ greet는 인자가 두 개 필요해요 | ^^^^^---------- 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는 인자가 두 개 필요해요 | ++++++++++++“이 함수는 인자를 2개 받는데 1개만 넘겼어요. &str 타입의 2번째 인자가 빠졌어요”라는 뜻이에요.
이 에러가 나는 프로그램은 이거예요.
fn greet(name: &str, greeting: &str) { println!("{greeting}, {name}!");}
fn main() { greet("Ferris"); // ❌ greet는 인자가 두 개 필요해요}인자가 너무 많아도 같은 코드가 나와요. 이때는 “unexpected argument”(예상하지 못한 인자) 라벨과 “remove the extra 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}`점으로 호출한 메서드라면 첫 줄이 “this method takes…”(이 메서드는 …를 받는다)로 바뀌어요. Rgb(255, 0) 같은 튜플 구조체라면 “this struct takes 3 arguments”(이 구조체는 인자 3개를 받는다)라고 해요. 모두 같은 에러예요.
무슨 뜻인가요
섹션 제목: “무슨 뜻인가요”메시지는 세 부분으로 되어 있고, 하나하나가 도움이 돼요.
- 첫 줄은 개수를 세요. 함수는 인자 2개를 원하는데 1개를 넘겼어요.
- 호출 아래의 라벨은 문제를 짚어 줘요. “argument #2 of type
&stris missing”(&str타입의 2번째 인자가 빠졌다). note:줄은 함수 정의로 가서, 빠뜨린 매개변수greeting: &str에 밑줄을 그어요.
help: 줄은 인자가 들어갈 자리를 보여 줘요. /* &str */는 타입을 알려 주는 자리 표시일 뿐이에요. 주석이라서 그대로 붙여 넣으면 컴파일되지 않아요. 그 자리에 "안녕하세요" 같은 진짜 값을 넣으세요.
컵과 동전이 모두 있어야 하는 커피 자판기와 비슷해요. 동전만 넣는다고 커피가 반 잔 나오지는 않아요. 그냥 작동하지 않죠.
Rust가 이렇게 하는 이유
섹션 제목: “Rust가 이렇게 하는 이유”Rust에서 함수의 시그니처는 정확한 계약이에요. 이 매개변수들을, 이 순서로, 이 타입으로. 모든 호출이 여기에 맞아야 해요. 기본 인자(greeting = "Hello")도 없고, 오버로딩(매개변수만 다른 greet 함수 두 개)도 없고, 인자를 “몇 개든” 받는 함수도 없어요. 기본값을 적으려고 하면 error: parameter defaults are not supported(매개변수 기본값은 지원되지 않는다)가 나고, greet를 하나 더 만들면 E0428, “the name greet is defined multiple times”(greet라는 이름이 여러 번 정의되었다)가 나요.
엄격해 보이지만 그만한 보람이 있어요. greet(name, greeting)을 읽으면 어떤 함수가 실행되고 무엇을 받는지 정확히 알 수 있어요. 함수에 매개변수를 추가하면 컴파일러가 고쳐야 할 호출을 전부 알려 주니까, 빠뜨리는 곳이 생기지 않아요. (println!처럼 값을 몇 개든 받는 것들은 매크로예요. !가 바로 그 표시예요. 매크로를 참고하세요.)
매개변수가 어떻게 동작하는지는 함수에서 다뤄요.
고치는 방법
섹션 제목: “고치는 방법”방법 1: 시그니처가 요구하는 대로 정확히 넘기기
섹션 제목: “방법 1: 시그니처가 요구하는 대로 정확히 넘기기”거의 언제나 이 방법을 고르세요. note:가 가리키는 곳을 열고, 매개변수 목록을 읽고, 매개변수마다 값을 하나씩 순서대로 넘기세요.
fn greet(name: &str, greeting: &str) { println!("{greeting}, {name}!");}
fn main() { greet("Ferris", "안녕하세요"); greet("Corro", "좋은 아침이에요");}실행 결과:
안녕하세요, Ferris!좋은 아침이에요, Corro!함수를 막 바꿨더니 호출이 전부 깨졌다면, 그건 컴파일러가 도와주고 있는 거예요. 컴파일러가 알려 주는 호출을 하나씩 고치거나, 새 매개변수가 정말 선택 사항이어야 하는 건 아닌지 생각해 보세요(방법 3).
방법 2: 메서드는 점으로 호출하기
섹션 제목: “방법 2: 메서드는 점으로 호출하기”함수에 self 매개변수가 있을 때 이 방법을 고르세요. 메서드는 보통 value.method(args)처럼 호출해요. 점 앞의 값이 self를 채우니까, 그건 세지 않아요. 대신 Type::method(args)라고 쓰면 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); | +++++++++++++++++++“&mut Account 타입의 1번째 인자가 빠졌어요”라는 뜻이에요.
개수를 세는 방식을 눈여겨보세요. Account::deposit(...)으로 호출하면 rustc는 함수가 인자 2개를 받는다고 해요. &mut self도 세기 때문이에요. acc.deposit()으로 호출하면 메서드가 인자 1개를 받는다고 해요. 점이 이미 self를 채워 주니까요. 인자를 다 채우면 두 형태 모두 동작해요.
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); // 보통 쓰는 방법 Account::deposit(&mut acc, 25); // 같은 호출을 풀어서 쓴 것 println!("잔액: {}", acc.balance);}실행 결과:
잔액: 75평소 코드에서는 점 형태를 쓰세요. Type::method(&x, a)는 메서드 자체를 어딘가에 넘겨야 할 때 유용해요. team.iter().map(Player::label) 같은 경우죠(E0424의 예제). 함수에 self가 전혀 없다면(new처럼) Type::function(...)으로만 호출할 수 있어요. 구조체와 E0424를 참고하세요.
방법 3: 가끔만 필요한 인자에는 Option<T> 쓰기
섹션 제목: “방법 3: 가끔만 필요한 인자에는 Option<T> 쓰기”인자 하나에 대개 적당한 기본값이 있을 때 이 방법을 고르세요. 그 인자를 Option으로 만들고, “기본값을 써 달라”는 뜻으로 None을 넘기고, unwrap_or로 채우세요.
fn connect(host: &str, port: u16, timeout_secs: Option<u64>) { let timeout = timeout_secs.unwrap_or(30); println!("{host}:{port}에 연결하는 중 (타임아웃 {timeout}초)");}
fn main() { connect("localhost", 8080, None); connect("example.com", 443, Some(5));}실행 결과:
localhost:8080에 연결하는 중 (타임아웃 30초)example.com:443에 연결하는 중 (타임아웃 5초)솔직히 말하면, 호출하는 쪽은 여전히 None이라는 인자를 적어야 해요. Rust는 오히려 그걸 좋아해요. 기본값을 쓰고 있다는 게 호출에 드러나니까요. 아예 적고 싶지 않다면, 이름이 분명한 함수 두 개를 만드세요. 예를 들어 connect(host, port)가 connect_with_timeout(host, port, 30)을 호출하게 하는 거예요. 이게 오버로딩 대신 Rust에서 흔히 쓰는 답이에요. Option을 참고하세요.
방법 4: Default를 구현한 설정 구조체 쓰기
섹션 제목: “방법 4: Default를 구현한 설정 구조체 쓰기”함수에 설정이 많은데 대부분의 호출은 한두 개만 바꿀 때 이 방법을 고르세요. 설정을 구조체에 넣고 Default를 구현한 다음, 호출하는 쪽은 ..Default::default()로 필요한 것만 채우게 하세요.
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!( "{}:{}에서 대기 중 (타임아웃 {}초, verbose: {})", config.host, config.port, config.timeout_secs, config.verbose );}
fn main() { start(ServerConfig::default()); start(ServerConfig { port: 3000, verbose: true, ..Default::default() });}실행 결과:
localhost:8080에서 대기 중 (타임아웃 30초, verbose: false)localhost:3000에서 대기 중 (타임아웃 30초, verbose: true)이렇게 하면 이름 붙은 인자 비슷한 것도 얻을 수 있어요. port: 3000을 보면 그 숫자가 무슨 뜻인지 알 수 있잖아요. 모든 필드의 기본값이 그 타입 자체의 기본값(0, false, 빈 문자열)이라면 #[derive(Default)]가 impl을 대신 써 줘요. 더 큰 API라면 빌더 패턴(Server::builder().port(3000).build())이 다음 단계예요. 구조체와 디자인 패턴을 참고하세요.
자주 보게 되는 곳
섹션 제목: “자주 보게 되는 곳”매개변수를 추가한 뒤. fn save(path: &str)에 두 번째 매개변수를 추가하면, 예전의 save("notes.txt") 호출이 모두 E0061로 깨져요. 해결: 컴파일러가 알려 주는 호출을 하나씩 고치거나, 새 값이 대개 같다면 방법 3이나 방법 4를 쓰세요.
표준 라이브러리 메서드. names.push("Cy", "Di")는 “this method takes 1 argument but 2 arguments were supplied”(이 메서드는 인자 1개를 받는데 2개를 넘겼다)를 내요. push는 아이템을 하나씩 추가해요. 해결: 두 번 호출하거나 names.extend(["Cy", "Di"])를 쓰세요. 벡터를 참고하세요.
튜플 구조체. struct Rgb(u8, u8, u8);에서 Rgb(255, 0)이라고 쓰면 “this struct takes 3 arguments but 2 arguments were supplied”(이 구조체는 인자 3개를 받는데 2개를 넘겼다)가 나요. 해결: 모든 필드를 채우세요. Rgb(255, 0, 0).
다른 언어에서 온 습관. Python의 greet("Ferris", greeting="Hi")는 Rust 문법이 아니에요. greet(greeting = "Hi")라고 쓰면 인자에 이름을 붙이는 게 아니에요. rustc는 이걸 greeting이라는 변수에 대입하는 걸로 읽고, E0425도 함께 알려 줘요. 해결: 값을 위치 순서대로 넘기거나, 설정 구조체를 쓰세요(방법 4).
클로저. add가 |a: i32, b: i32| a + b일 때 add(1)을 호출하면 같은 메시지가 나오지만, 코드는 E0057 (클로저 버전)이에요. 해결책도 같아요. 두 값을 모두 넘기세요.
관련 에러
섹션 제목: “관련 에러”- E0308: 인자 개수는 맞는데 하나의 타입이 틀린 경우예요. 두 인자의 순서가 바뀐 경우가 많아요.
- E0424:
self매개변수가 없는 함수에서self를 쓴 경우로, 메서드와 연관 함수의 반대편 이야기예요. - E0599: 그런 이름의 메서드가 없는 경우예요. 예를 들어
new같은 연관 함수를 점으로 호출할 때요. - E0425: 값을 찾을 수 없다는 에러로, 이름 붙은 인자를 써 보려고 할 때 E0061과 함께 나올 수 있어요.
- 모든 컴파일러 에러: 에러 페이지 전체 목록이에요. 관련된 것으로 E0057(클로저를 잘못된 개수의 인자로 호출)과, 따로 페이지는 없지만 E0428(오버로딩하려다 함수를 두 번 정의)도 있어요.
이해도 점검
섹션 제목: “이해도 점검”이해도 점검
1. fn area(w: u32, h: u32) -> u32를 area(5)로 호출했어요. rustc는 뭐라고 할까요?
2. 메서드가 fn deposit(&mut self, amount: i64)예요. 올바른 호출은?
3. 대부분의 호출에서는 신경 쓰지 않는 timeout 인자를 만들고 싶어요. Rust에서 흔히 쓰는 방법은?
점수: 0 / 3
더 알아보기
섹션 제목: “더 알아보기”- E0061 — Rust error codes index (영어): 이 에러에 대한 공식 설명이에요.
- 함수 — The Rust Programming Language 한국어판: 매개변수, 시그니처의 타입, 함수 호출이 어떻게 동작하는지 다뤄요.
- 메서드 문법 — The Rust Programming Language 한국어판: 왜 점이
self를 채워 주는지, 그리고new같은 연관 함수를 설명해요. Default트레이트 — 표준 라이브러리 문서 (영어):Default파생과..Default::default()사용법이에요.- Builder pattern — Rust Design Patterns (영어): 긴 인자 목록과 선택적 인자 대신 흔히 쓰는 패턴이에요.