Skip to content

Cannot use std::iter::try_collect after importing itertools #666

Open

Description

@ShiveshM

The try_collect method in itertools is shadowing the method of the same name in std::iter on nightly :
https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.try_collect

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=97cdf9666d33eeca8a69acdd1e59948e

#![allow(unused)]
#![feature(iterator_try_collect)]

use itertools::Itertools; // 0.10.5

fn main() {
    let u = vec![Some(1), Some(2), Some(3)];
    let v = u.into_iter().try_collect::<Vec<i32>>();
    assert_eq!(v, Some(vec![1, 2, 3]));
}
Compiling playground v0.0.1 (/playground)
error[[E0107]](https://doc.rust-lang.org/nightly/error-index.html#E0107): this associated function takes 3 generic arguments but 1 generic argument was supplied
    --> src/main.rs:8:27
     |
8    |     let v = u.into_iter().try_collect::<Vec<i32>>();
     |                           ^^^^^^^^^^^   -------- supplied 1 generic argument
     |                           |
     |                           expected 3 generic arguments
     |
note: associated function defined here, with 3 generic parameters: `T`, `U`, `E`
    --> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/itertools-0.10.5/src/lib.rs:2009:8
     |
2009 |     fn try_collect<T, U, E>(self) -> Result<U, E>
     |        ^^^^^^^^^^^ -  -  -
help: add missing generic arguments
     |
8    |     let v = u.into_iter().try_collect::<Vec<i32>, U, E>();
     |                                                 ++++++

error[[E0271]](https://doc.rust-lang.org/nightly/error-index.html#E0271): expected `IntoIter<Option<{integer}>>` to be an iterator that yields `Result<Vec<i32>, _>`, but it yields `Option<{integer}>`
    --> src/main.rs:8:27
     |
8    |     let v = u.into_iter().try_collect::<Vec<i32>>();
     |                           ^^^^^^^^^^^ expected enum `Result`, found enum `Option`
     |
     = note: expected enum `Result<Vec<i32>, _>`
                found enum `Option<{integer}>`
note: the method call chain might not have had the expected associated types
    --> src/main.rs:8:15
     |
7    |     let u = vec![Some(1), Some(2), Some(3)];
     |             ------------------------------- this expression has type `Vec<Option<{integer}>>`
8    |     let v = u.into_iter().try_collect::<Vec<i32>>();
     |               ^^^^^^^^^^^ `Iterator::Item` is `Option<{integer}>` here
note: required by a bound in `itertools::Itertools::try_collect`
    --> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/itertools-0.10.5/src/lib.rs:2011:32
     |
2011 |         Self: Sized + Iterator<Item = Result<T, E>>,
     |                                ^^^^^^^^^^^^^^^^^^^ required by this bound in `Itertools::try_collect`

error[[E0308]](https://doc.rust-lang.org/nightly/error-index.html#E0308): mismatched types
 --> src/main.rs:9:5
  |
9 |     assert_eq!(v, Some(vec![1, 2, 3]));
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Result`, found enum `Option`
  |
  = note: expected enum `Result<_, _>`
             found enum `Option<Vec<{integer}>>`
  = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)

Some errors have detailed explanations: E0107, E0271, E0308.
For more information about an error, try `rustc --explain E0107`.
error: could not compile `playground` due to 3 previous errors

Activity

  1. Philippe-Cholet commented on Oct 7, 2023

    @Philippe-Cholet
    Member

    I think this should be fixed once RFC2845 (#702 (comment)) is implemented, as one should then shadow the other.

  2. git-staus commented on Aug 21, 2025

    @git-staus

    If I am not mistaken RFC2845 has been merged now, but the impossibility to use (nightly) standard try_collect when Itertools is in scope persists. This has been mentioned in #402, #710, #830, #844, #934, and #951 already and I understand there is an ungoing discussion about naming conflicts in general, but I'd like to add my voice to this issue anyway.

    I have been chasing my tail for quite some time yesterday with code along the following lines:

    #![feature(iterator_try_collect)]
    
    #[cfg(test)]
    mod tests {
        use itertools::Itertools as _;
        use std::num::ParseIntError;
    
        #[test]
        /// Random functionality that requires `itertools::Itertools::counts`
        fn collect() -> Result<(), ParseIntError> {
            let freqs = ["1", "1", "1", "2", "3", "3"]
                .into_iter()
                .map(|val| val.parse::<u32>())
                .collect::<Result<Vec<_>, _>>()?
                .into_iter()
                .counts();
            assert_eq!(*freqs.get(&3).unwrap(), 2);
            Ok(())
        }
    
        #[test]
        /// As above, but tries to shorten `collect::<Result<Vec<_>, _>>` with `try_collect` from `std`
        fn iter_try_collect() -> Result<(), ParseIntError> {
                let freqs = ["1", "1", "1", "2", "3", "3"]
                    .into_iter()
                    .map(|val| val.parse::<u32>())
                    // "error[E0107]: method takes 3 generic arguments but 1 generic argument was supplied"
                    // this error indicates that this always resolves to `itertools::Itertools::try_collect`
                    .try_collect::<Vec<_>>()?
                    .into_iter()
                    .counts();
                assert_eq!(*freqs.get(&3).unwrap(), 2);
            Ok(())
        }
        
        #[test]
        /// Uses the more clunky `itertools::Itertools::try_collect` instead of standard `try_collect`
        fn itertools_try_collect() -> Result<(), ParseIntError> {
            let freqs = ["1", "1", "1", "2", "3", "3"]
                .into_iter()
                .map(|val| val.parse::<u32>())
                .try_collect::<_, Vec<_>, _>()?
                .into_iter()
                .counts();
            assert_eq!(*freqs.get(&3).unwrap(), 2);
            Ok(())
        }
    
        #[test]
        /// Uses standard `try_collect` but with unambiguous syntax to avoid the conflict,
        /// ruining any convenience gained from this method...
        fn iter_try_collect_ugly() -> Result<(), ParseIntError> {
            let freqs = std::iter::Iterator::try_collect::<Vec<_>>(
                &mut ["1", "1", "1", "2", "3", "3"]
                    .into_iter()
                    .map(|val| val.parse::<u32>()),
            )?
            .into_iter()
            .counts();
            assert_eq!(*freqs.get(&3).unwrap(), 2);
            Ok(())
        }
    }

    I am quite fond of the (slighly more flexible) try_collect from std, but in this case I needed counts as well. As of yet (itertools 0.14.0) I haven't found a decent way to avoid this conflict...

  3. git-staus commented on Aug 21, 2025

    @git-staus

    Another idea I have not seen mentioned before: how about introducing an on-by-default feature flag? (e.g. breaks-nightly or might-conflict or whatever) This way itertools functionality that might conflict could simply be put behind such a flag without introducing breakage, while still providing those on nightly with a way to use all the rest of itertools.

    I am happy to draft a PR if this is deemed a viable approach.

  4. git-staus commented on Aug 21, 2025

    @git-staus

    Despite the parallel naming in itertools and the nightly standard library, it seems the name of try_collect is currently still subject to change (tracking issue). I guess this weakens any arguments for solving this conflict from the itertools front...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions