Rust Error E0593: Closure Is Expected to Take a Single 2-Tuple as Argument
E0593 means you handed a closure (or a function) to something else, like map, sort_by or thread::spawn, and its number of parameters doesn’t match how it will be called. The most common case by far: an iterator gives your closure one tuple, and you wrote two parameters. It’s a one-character fix most of the time, and rustc usually writes it for you. If you’re the one calling the closure with the wrong number of values, that’s E0057 instead. For the other errors in this series, see Rust compiler errors.
The error message
Section titled “The error message”error[E0593]: closure is expected to take a single 2-tuple as argument, but it takes 2 distinct arguments --> src/main.rs:6:10 |6 | .map(|i, name| format!("{}. {name}", i + 1)) // ❌ two parameters | ^^^ --------- takes 2 distinct arguments | | | expected closure that takes a single 2-tuple as argument |help: change the closure to accept a tuple instead of individual arguments |6 - .map(|i, name| format!("{}. {name}", i + 1)) // ❌ two parameters6 + .map(|(i, name)| format!("{}. {name}", i + 1)) // ❌ two parameters |This is the program that produces it:
fn main() { let names = vec!["Ferris", "Corro", "Rusty"]; let lines: Vec<String> = names .iter() .enumerate() .map(|i, name| format!("{}. {name}", i + 1)) // ❌ two parameters .collect(); println!("{:?}", lines);}E0593 also comes with a few other first lines. They all mean “wrong number of parameters”:
error[E0593]: closure is expected to take 2 arguments, but it takes 1 argumenterror[E0593]: closure is expected to take 0 arguments, but it takes 1 argumenterror[E0593]: function is expected to take 1 argument, but it takes 2 argumentserror[E0593]: closure is expected to take 2 distinct arguments, but it takes a single 2-tuple as argumentWhat it means
Section titled “What it means”Look at the two markers under line 6:
^^^points atmap. That’s the one calling your closure, and it decides how many values it passes.---------points at your parameter list|i, name|. That’s what you promised to accept.
They don’t agree. enumerate() turns every item into one tuple, (index, item), and map passes that tuple to your closure as one argument. Your closure asks for two separate ones.
Think of a mail slot that takes one envelope at a time. You can put two letters inside the envelope (a tuple), but the slot won’t take two loose letters. |(i, name)| says “give me the envelope, and I’ll open it right here”.
The word “closure” or “function” in the message tells you what you passed: a closure written in place, or a named fn like .map(add).
Why Rust does this
Section titled “Why Rust does this”Every method that takes a closure says exactly how it will call it. map wants something like FnMut(Self::Item) -> B: one argument, the item. For enumerate() the item is (usize, &&str). That’s one value that happens to be a tuple. sort_by wants FnMut(&T, &T) -> Ordering: two arguments. thread::spawn wants FnOnce() -> T: none at all.
Rust never splits a tuple into separate arguments for you, and never packs separate arguments into a tuple. That keeps the rule simple: what the method’s signature says is what your closure receives. Once you can read these signatures, you can predict what any iterator method hands you.
You can read more about the Fn traits behind this in Closure traits, and about tuples in Destructuring a tuple.
How to fix it
Section titled “How to fix it”Fix 1: Unpack the tuple inside the parameter list
Section titled “Fix 1: Unpack the tuple inside the parameter list”Choose this when the message says “a single 2-tuple”. This is the fix you’ll need most often. Add parentheses: |(i, name)| takes one tuple and gives its two parts names. It works for enumerate, zip, and iterating over a map:
use std::collections::BTreeMap;
fn main() { let names = vec!["Ferris", "Corro", "Rusty"];
// enumerate gives one (index, name) tuple per item let lines: Vec<String> = names .iter() .enumerate() .map(|(i, name)| format!("{}. {name}", i + 1)) .collect(); println!("{:?}", lines);
// iterating a map also gives one (key, value) tuple per item let mut stock = BTreeMap::new(); stock.insert("apples", 3); stock.insert("pears", 0); stock.insert("plums", 5); let total: i32 = stock.iter().map(|(_name, count)| *count).sum(); println!("{total} pieces of fruit");}Output:
["1. Ferris", "2. Corro", "3. Rusty"]8 pieces of fruitThe _ at the start of _name tells Rust “I know I’m not using this”. You can also write just _. The same idea for zip is shown on the E0057 page.
Fix 2: Give the method the number of parameters it calls with
Section titled “Fix 2: Give the method the number of parameters it calls with”Choose this when the message says “expected to take N arguments”. Check what the method passes, then write exactly that many parameters:
sort_bycompares two items:|a, b|.for_each,map,filterpass one item:|item|, or|_|if you don’t need it.thread::spawnpasses nothing:||, usuallymove ||.
use std::thread;
fn main() { // sort_by compares two items at a time: two parameters let mut scores = vec![42, 7, 99, 15]; scores.sort_by(|a, b| b.cmp(a)); println!("{:?}", scores);
// for_each hands over one item: one parameter (use _ if you don't need it) let tasks = vec!["write", "test", "ship"]; tasks.iter().for_each(|task| println!("todo: {task}")); tasks.iter().for_each(|_| print!("*")); println!();
// thread::spawn calls the closure with nothing: zero parameters let id = 1; let handle = thread::spawn(move || format!("worker {id} done")); println!("{}", handle.join().unwrap());}Output:
[99, 42, 15, 7]todo: writetodo: testtodo: ship***worker 1 doneTo pass data into a thread, don’t add a parameter. Capture it with move, like id above. See Concurrency and Sorting.
Fix 3: Wrap a named function, or make it take a tuple
Section titled “Fix 3: Wrap a named function, or make it take a tuple”Choose this when the message says “function is expected to take…”. You passed a function by name, like .map(add), and its parameters don’t fit. Here the items are pairs, but add takes two numbers:
error[E0593]: function is expected to take a single 2-tuple as argument, but it takes 2 distinct arguments --> src/main.rs:7:48 |1 | fn add(a: i32, b: i32) -> i32 { | ----------------------------- takes 2 distinct arguments...7 | let sums: Vec<i32> = pairs.into_iter().map(add).collect(); | --- ^^^ expected function that takes a single 2-tuple as argument | | | required by a bound introduced by this callYou can wrap the function in a small closure that unpacks the tuple and calls it, or write a version that takes the tuple directly:
fn add(a: i32, b: i32) -> i32 { a + b}
// a version that takes the pair as one tuplefn add_pair((a, b): (i32, i32)) -> i32 { a + b}
fn main() { let pairs = vec![(1, 2), (3, 4), (10, 20)];
// wrap: one tuple in, two arguments out let sums: Vec<i32> = pairs.iter().map(|&(a, b)| add(a, b)).collect(); println!("{:?}", sums);
// or pass a function that already takes one tuple let sums2: Vec<i32> = pairs.into_iter().map(add_pair).collect(); println!("{:?}", sums2);}Output:
[3, 7, 30][3, 7, 30]The & in |&(a, b)| is there because .iter() lends each pair as &(i32, i32), and the pattern copies the two numbers out. The wrapper is usually the better choice: add stays a normal two-argument function that the rest of your code can call.
If your own generic function wants Fn(i32) -> i32 and you pass a two-argument add, you get “function is expected to take 1 argument, but it takes 2 arguments”. The fix is a wrapper that fills in the extra value, shown in E0057’s Fix 3.
Where you’ll usually see it
Section titled “Where you’ll usually see it”HashMap::retain (the other way around). Most map methods hand you a tuple, but retain passes key and value as two separate arguments. So stock.retain(|(name, count)| *count > 0) gives “closure is expected to take 2 distinct arguments, but it takes a single 2-tuple as argument”. Fix: drop the parentheses, stock.retain(|_name, count| *count > 0). Vec::retain takes one item, as usual.
Forgetting the parameter. tasks.iter().for_each(|| println!("task")) gives “closure is expected to take 1 argument, but it takes 0 arguments”, and rustc suggests |_|. Fix: |_|, or |task| if you want to use it.
Threads. thread::spawn(|id| ...) gives “closure is expected to take 0 arguments, but it takes 1 argument”. Fix: thread::spawn(move || ...) and capture id from outside.
A function pointer parameter. If your function takes f: fn(i32) -> i32 (a plain function pointer, not a generic F: Fn), passing a two-argument add gives E0308 instead, with the label “incorrect number of function parameters”. Same fix: a wrapper closure like |x| add(x, 10). Closures that don’t capture anything can be passed as fn pointers.
Related errors
Section titled “Related errors”- E0057: closure takes N arguments but M were supplied: you call a closure yourself with the wrong number of values.
- E0631: type mismatch in closure arguments: the count is right, but a parameter has the wrong type, often
i32where the iterator gives&i32. - E0061: function takes N arguments but M were supplied: calling a normal function with the wrong number of arguments.
- E0308: mismatched types: what you get with a
fn(...)pointer parameter, or with|(a, b)|when the items aren’t tuples at all. - E0599: no method found: sometimes shows up right after E0593, for example “the method
collectexists … but its trait bounds were not satisfied”. Fix E0593 first and it goes away. - All compiler errors: the full list of error pages.
Quick check
Section titled “Quick check”Quick check
1. names.iter().enumerate().map(|i, name| ...) gives E0593. What is the fix?
2. How many parameters does the closure for scores.sort_by(...) need?
3. thread::spawn(|id| println!("{id}")) gives E0593. How do you get id into the thread?
Score: 0 / 3
Read more
Section titled “Read more”- E0593 — Rust error codes index: the official explanation of this error.
- Closures — The Rust Book: closure syntax, how parameter types are inferred, and the
Fntraits. - As input parameters — Rust by Example: how a function’s
Fnbound decides what closure it accepts. Iterator::enumerate— standard library docs: shows that each item becomes one(index, value)tuple.HashMap::retain— standard library docs: the map method whose closure takes key and value as two arguments.