Explanation for difference between `.filter(func)` and `.filter(|x| func(x))`?

I feel like deja vu, I had literally just gone on a bit of a rant about issues just like this yesterday: Rust beginner notes & questions The issue is more pervasive than just the closure-to-function refactoring! A wide range of trivial code changes make the compiler throw its hands up in the air unpredictably:

fn foo(x: u8) {
    println!("{}", x);
}

fn main() {
    let buf: &[u8] = b"foobar";

    // This is a pattern I often follow, I start with  iterator-based code:
    buf.iter().for_each(|x| println!("{}", x));

    // But when the closures get a bit too complex I like to extract them into dedicated functions. 
    // However, this doesn't compile!
    buf.iter().for_each(foo);
    
    // No problems here though, despite no visible use of the deref operator. In a complex
    // expression this is a nightmare to trace, even with  the help of IntelliJ's Rust plugin!
    buf.iter().map( |x| x + 1 ).for_each( foo );
    
    // This compiles.
    let _x: u8;
    let _x = buf.iter().next().unwrap();
    
    // But a *trivial* simplification I expect to work doesn't compile! Wat.
    let _y: u8 = buf.iter().next().unwrap();
}

I very strongly feel that this is just one example of a very serious issue with Rust's library design and everyone needs to stop and think carefully about the direction it's taking.

Layer upon layer of ad-hoc automagic leads to unpredictable, anti-human results that make things not just worse in the long run, but can absolutely destroy languages. JavaScript is basically unsalvageable now, and its usage persists only because of sheer momentum. It is essentially mathematically impossible to make a refactoring IDE for C++ with anywhere near the same capabilities as IntelliJ or Visual Studio because of design decisions like the ones I posted. Rust is already dangerously close to becoming forever impossible to process with tooling of this type, or any other type.

It's now getting difficult for humans to grok too, and for no good reason. I'm not talking about the borrow checker, or the necessary difficulties inherent to system programming, but "surprising" foot-guns like this that just make no sense to anyone used to either mathematics or any other sane programming language:

let a: OsString = OsString::from( "foo" );
let b: &str = "bar";
if let Some(_) = a.partial_cmp( b ) { /* Compiles fine! */ };
if let Some(_) = b.partial_cmp( a ) { /* LOL, no. */ };
9 Likes