Returning a new lifetime vs returning 'static lifetime

Hi everyone, this might be a basic question about lifetimes but I wanted to make sure if I understand it properly. I have two questions about the code snippet at the bottom:

  1. (part 1): are the function signatures foo and bar below equivalent? If there is no relation between 'a and 'b, does it always mean 'b can be replaced by 'static? I cannot think of a reason why not. bar's function signature is more readable than foo's. If they are equivalent, I expected clippy to suggest writing in bar form, but I did not get any warnings with clippy.
  2. (part 2): Why doesn't let _: T1 = baz throw a compile error? From what I understand, T1 is a function that takes a Foo with some lifetime 'a as input and returns a Foo that can live forever. Clearly baz does not define such a function. Is there something wrong with my understanding?
struct Foo<'a>(#[allow(unused)] &'a str);

fn foo<'a, 'b>(_a: Foo<'a>) -> Foo<'b> {
    Foo("")
}

fn bar<'a>(_a: Foo<'a>) -> Foo<'static> {
    Foo("")
}

fn baz<'a>(_a: Foo<'a>) -> Foo<'a> {
    Foo("")
}

type T1<'a> = fn(Foo<'a>) -> Foo<'static>;
type T2<'a> = fn(Foo<'a>) -> Foo<'a>;

fn main() {
    let s = String::from("abc");
    
    let x = Foo(s.as_str());
    let y = Foo(s.as_str());
    let _z = Foo(s.as_str());
    
    // part 1
    let _: Foo<'static> = foo(x);
    let _: Foo<'static> = bar(y);
    // let _: Foo<'static> = baz(_z); // error
    
    // part 2
    let _: T1 = foo;
    let _: T2 = foo;
    
    let _: T1 = bar;
    let _: T2 = bar;
    
    let _: T1 = baz;
    let _: T2 = baz;
}

playground link: Rust Playground

Because T1 is an incomplete type, that assignment unifies to let _: T1<'static> = baz; // fn(Foo<'static>) -> Foo<'static>
You meant type T1 = for<'a> fn(Foo<'a>) -> Foo<'static> probably.

For most practical uses, yes. But in a technical sense, no.

fn check<'a, F>(_: &'a str, _: F)
where
    F: Fn(Foo<'_>) -> Foo<'a>
    // Or perhaps more realistically, F: Fn(Foo<'a>) -> Foo<'a>
{}

// Works
check(&s, foo);
// Fails
check(&s, bar);
// Works
check(&s, |foo| bar(foo));

Going from foo to bar can be a breaking change.

You can think of the functions' capabilities like so. Side note that foo is parameterized by a lifetime but bar is not.

impl<'a, 'b> Fn(Foo<'a>) -> Foo<'b> for foo<'b> {
    type Output = Foo<'b>;
    fn call(&self, args: (Foo<'a>,)) -> Self::Output { ... }
}

impl<'a> Fn(Foo<'a>) -> Foo<'static> for bar {
    type Output = Foo<'static>;
    fn call(&self, args: (Foo<'a>,)) -> Self::Output { ... }
}

baz can return the 'static lifetime, if the input lifetime is also 'static.

    let _: T1<'static> = baz/* ::<'static> */;

@ProgramCrafter is probably right that

i.e. the confusion is the difference between "there exists an input lifetime for which 'static can be returned" and "for all input lifetimes 'static can be returned".