| # `impl Trait` |
| |
| `impl Trait` can be used in two locations: |
| |
| 1. as an argument type |
| 2. as a return type |
| |
| ## As an argument type |
| |
| If your function is generic over a trait but you don't mind the specific type, you can simplify the function declaration using `impl Trait` as the type of the argument. |
| |
| For example, consider the following code: |
| |
| ```rust,editable |
| fn parse_csv_document<R: std::io::BufRead>(src: R) -> std::io::Result<Vec<Vec<String>>> { |
| src.lines() |
| .map(|line| { |
| // For each line in the source |
| line.map(|line| { |
| // If the line was read successfully, process it, if not, return the error |
| line.split(',') // Split the line separated by commas |
| .map(|entry| String::from(entry.trim())) // Remove leading and trailing whitespace |
| .collect() // Collect all strings in a row into a Vec<String> |
| }) |
| }) |
| .collect() // Collect all lines into a Vec<Vec<String>> |
| } |
| ``` |
| |
| `parse_csv_document` is generic, allowing it to take any type which implements BufRead, such as `BufReader<File>` or `[u8]`, |
| but it's not important what type `R` is, and `R` is only used to declare the type of `src`, so the function can also be written as: |
| |
| ```rust,editable |
| fn parse_csv_document(src: impl std::io::BufRead) -> std::io::Result<Vec<Vec<String>>> { |
| src.lines() |
| .map(|line| { |
| // For each line in the source |
| line.map(|line| { |
| // If the line was read successfully, process it, if not, return the error |
| line.split(',') // Split the line separated by commas |
| .map(|entry| String::from(entry.trim())) // Remove leading and trailing whitespace |
| .collect() // Collect all strings in a row into a Vec<String> |
| }) |
| }) |
| .collect() // Collect all lines into a Vec<Vec<String>> |
| } |
| ``` |
| |
| Note that using `impl Trait` as an argument type means that you cannot explicitly state what form of the function you use, i.e. `parse_csv_document::<std::io::Empty>(std::io::empty())` will not work with the second example. |
| |
| ## As a return type |
| |
| If your function returns a type that implements `MyTrait`, you can write its |
| return type as `-> impl MyTrait`. This can help simplify your type signatures quite a lot! |
| |
| ```rust,editable |
| use std::iter; |
| use std::vec::IntoIter; |
| |
| // This function combines two `Vec<i32>` and returns an iterator over it. |
| // Look how complicated its return type is! |
| fn combine_vecs_explicit_return_type( |
| v: Vec<i32>, |
| u: Vec<i32>, |
| ) -> iter::Cycle<iter::Chain<IntoIter<i32>, IntoIter<i32>>> { |
| v.into_iter().chain(u.into_iter()).cycle() |
| } |
| |
| // This is the exact same function, but its return type uses `impl Trait`. |
| // Look how much simpler it is! |
| fn combine_vecs( |
| v: Vec<i32>, |
| u: Vec<i32>, |
| ) -> impl Iterator<Item=i32> { |
| v.into_iter().chain(u.into_iter()).cycle() |
| } |
| |
| fn main() { |
| let v1 = vec![1, 2, 3]; |
| let v2 = vec![4, 5]; |
| let mut v3 = combine_vecs(v1, v2); |
| assert_eq!(Some(1), v3.next()); |
| assert_eq!(Some(2), v3.next()); |
| assert_eq!(Some(3), v3.next()); |
| assert_eq!(Some(4), v3.next()); |
| assert_eq!(Some(5), v3.next()); |
| println!("all done"); |
| } |
| ``` |
| |
| More importantly, some Rust types can't be written out. For example, every |
| closure has its own unnamed concrete type. Before `impl Trait` syntax, you had |
| to allocate on the heap in order to return a closure. But now you can do it all |
| statically, like this: |
| |
| ```rust,editable |
| // Returns a function that adds `y` to its input |
| fn make_adder_function(y: i32) -> impl Fn(i32) -> i32 { |
| let closure = move |x: i32| { x + y }; |
| closure |
| } |
| |
| fn main() { |
| let plus_one = make_adder_function(1); |
| assert_eq!(plus_one(2), 3); |
| } |
| ``` |
| |
| You can also use `impl Trait` to return an iterator that uses `map` or `filter` |
| closures! This makes using `map` and `filter` easier. Because closure types don't |
| have names, you can't write out an explicit return type if your function returns |
| iterators with closures. But with `impl Trait` you can do this easily: |
| |
| ```rust,editable |
| fn double_positives<'a>(numbers: &'a Vec<i32>) -> impl Iterator<Item = i32> + 'a { |
| numbers |
| .iter() |
| .filter(|x| x > &&0) |
| .map(|x| x * 2) |
| } |
| |
| fn main() { |
| let singles = vec![-3, -2, 2, 3]; |
| let doubles = double_positives(&singles); |
| assert_eq!(doubles.collect::<Vec<i32>>(), vec![4, 6]); |
| } |
| ``` |