#Lifetimes, early | late bindings, HRTBs.

53 messages · Page 1 of 1 (latest)

undone ether
#

So this code doesn't compile, because when I write 'a: 'b lifetime 'b' becomes early-bound https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=62782f2cdb398ed861b6df6196763ccf. Am I right? And that's why we
can't specify lifetime relations in the for<> block, it's just doesn't make sense because it forces lifetimes to become early-bound when the syntax was specifically designed to make them late-bound. yes?.. But how to live with it?

#

The challenge is how to make call_shortened_first compile without removing the 'a: 'b bound from the shortened_first?

onyx rock
undone ether
#

Of course I can, but I'm hacking HRTBs here.

onyx rock
#

well, do you have a representative example?

#

because it might be an XY

undone ether
#

Nope, just trying to figure out how they work and why I can't specify lifetime relations in the for<> block.

ashen swan
#

The "why" part is probably "rust doesn't support it yet"

onyx rock
#

I've seen that this feature has been wanted for a loong time

undone ether
#

Do you know is there an issue/RFC?

onyx rock
#

I can't find it now

#

I don't know if an RFC has been submitted

#

you can "add" bounds to hrtb functions with implicit bounds, like this: ```rs
fn shortened_first<'b, 'a: 'b>(first: &'a str, second: &'b str) -> &'b str {
first
}

fn call_shortened_first<F>(f: F)
where
F: for<'a, 'b> FnOnce(&'a str, &'b str, &'b &'a ()) -> &'b str
{
let a = "a".to_owned();
let b = "b".to_owned();

let result = f(&a, &b, &&());

println!("{result}")

}

fn main() {
let kek = "a";
let b = "b";

call_shortened_first(|a, b, _| shortened_first(a, b));    

}```

#

having &'b &'a () implies 'a: 'b

#

but you need the additional parameter

#

you can also possibly do this: ```rs
fn shortened_first<'b, 'a: 'b>(first: &'a str, second: &'b str) -> &'b str {
first
}

fn call_shortened_first<F>(f: F)
where
F: for<'a, 'b> FnOnce(&'b &'a str, &'b str) -> &'b str
{
let a = "a".to_owned();
let b = "b".to_owned();

let a = a.as_str();
let result = f(&a, &b);

println!("{result}")

}

fn main() {
call_shortened_first(|a, b| shortened_first(a, b));
}``` but calling it is kinda unergonomic (without the intermediate a variable it doesn't work)

undone ether
#

The first one is tricky

#
fn shortened_first<'b, 'a: 'b>(first: &'a str, second: &'b str, _ : &'b &'a ()) -> &'b str {
    first
}

fn call_shortened_first<F>(f: F) 
where 
    F: for<'a, 'b> FnOnce(&'a str, &'b str, &'b &'a ()) -> &'b str
{
    let a = "a".to_owned();
    let b = "b".to_owned();
    
    let result = f(&a, &b, &&());
    
    println!("{result}")
}


fn main() {
    let kek = "a";
    let b = "b";
    
    call_shortened_first(shortened_first);    
}```
#

This doesn't compile

#

The second one is an awesome hack! Thanks, will save it to my notes))))

onyx rock
#

in my opinion the first one makes more sense than the second

onyx rock
undone ether
onyx rock
undone ether
onyx rock
#

the issue is about inference for closures

onyx rock
undone ether
#

There are also examples with function pointers and traits

onyx rock
#

but the issue is about trait "aliases" for the Fn traits

#

I'm pretty sure that's a different issue

undone ether
onyx rock
#

yeah that's a bug

#

but not the one you linked

onyx rock
#

having an &'b &'a () argument doesn't change the hrtb

#

there is lots of issues in rustc regarding lifetime bounds and hrtb

#

that's a really complicated subject

undone ether
#

Yeah, so it looks like there needs to be an exception for lifetimes, so a lifetime bound by another lifetime won't make the second one early-bound. By the way, do I understand correctly that in 'a: 'b the 'b lifetime is early-bound?

undone ether
onyx rock
#

maybe both of them are

#

I'm not that well-versed in rustc internals

#

and that distinction isn't really taught anywhere

undone ether
onyx rock
#

you can try asking in #dark-arts, the official discord where rustc developers hang out, zulip or urlo

#

probably ask in #dark-arts where to ask this question lol

undone ether
#

Thank you! I will try.

onyx rock
#

let me know how it went 🙂

slate mango
#

@undone ether you can use an empty array to insert your implicit bounds in there

#

?eval

fn shortened_first<'b, 'a: 'b>(first: &'a str, second: &'b str) -> &'b str {
    first
}

fn call_shortened_first<F>(f: F) 
where 
    F: for<'a, 'b> FnOnce(&'a str, &'b str, [&'b &'a (); 0]) -> &'b str
{
    let a = "a".to_owned();
    let b = "b".to_owned();
    
    let result = f(&a, &b, []);
    
    println!("{result}")
}


fn main() {
    let kek = "a";
    let b = "b";
    
    call_shortened_first(|a, b, []| shortened_first(a, b));    
}
short mountainBOT
#
a
onyx rock