Clippy says redundant closure, but removing it causes rather mysterious compile error

I have a cut-down example below, and playground here.

It seems that without the closure, rustc does something not quite right with one of the lifetimes:

|
46 |     let result0 = nest(Succ::trace, &input);
   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected trait `for<'a> Fn<(&'a Succ<'a, '_, Zero>,)>`
              found trait `Fn<(&Succ<'_, '_, Zero>,)>`
note: the lifetime requirement is introduced here
use std::{
    cell::RefCell,
    fmt::Debug,
};

trait OpTrait {}

struct Op<T>(T);

impl<T> OpTrait for Op<T> {}

#[derive(Clone)]
struct Succ<'a, 't, T: 't>(T, &'a RefCell<Option<Box<dyn OpTrait + 't>>>);

impl<'a, 't, T: Clone + 't> Succ<'a, 't, T>
{
    fn trace(&self) -> Self {
        // what this does is unimportant
        let b = Box::new(Op(self.0.clone()));
        let _r = self.1.borrow_mut().insert(b);
        Succ(self.0.clone(), self.1)
    }
}

#[derive(Debug, Clone)]
struct Zero;

fn nest<'t, T: Clone + 't, F>(f: F, t: &T) -> T
where
    for<'a> F: Fn(&'a Succ<'a, 't, T>) -> Succ<'a, 't, T>,
{
    let trace = RefCell::new(None);
    let nested = Succ(t.clone(), &trace);
    let result = f(&nested);
    result.0
}

fn main() {
    let input = Zero;
    
    // ok - but redundant closure
    let result1 = nest(|n| n.trace(), &input);
    dbg!(result1);
    
    // boom
    let result0 = nest(Succ::trace, &input);
    dbg!(result0);
}

This doesn't block me - happy to keep the closures and add an allow, but it kinda piqued my interest. I can't figure out what is going wrong here or why a closure does work.

Cheers!

The lifetime binders are in the wrong spot for Succ::trace to be higher-ranked over them:

Succ::<'_, '_, Zero>::trace
//     ^^  ^^ each inferred to be a single, concrete lifetime

To meet the bound, trace would have to be the one with lifetime parameters.

The closure works around this by being higher-ranked over the lifetimes and, conceptually, choosing a different pair of Succ lifetimes on each call.

As for Clippy, I think it's this or very related, despite being closed. The lint seems to have a lot of issues.

Understood, thanks!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.