Apply 'static to the type in generic context

Consider this bound:

  • F: Send + core::future::Future
  • F::Output: Send + core::future::Future

And I need to pass it to the function that requires also 'static bound (sure, allocating on heap is easy, but it is also wasteful)
Is there a way to add 'static bound to both F and F::Output without resolving to heap allocations?
My code guarantees that F will live long enough.

P.s. F is passed by value

Correct me if I'm wrong, but only static variables and heap allocations may have 'static lifetime.

However, you can use std::mem::transmute to change variable's lifetime, if you're willing to use unsafe code.

in this context variable is owned and let's say worker, that is going to use it, will not be able to outlive it.
Requiring 'static there affects any Future that borrows (e.g. generated from async method)

I sure wish I could just use transmute, but there is a problem is that I cannot really do:
let f: F + 'static = unsafe { mem::transmute(self) }
It doesn't make much sense to compiler and using transmute together with await doesn't work because type will be ambigious unless I manually specify it (mostly because I also used in async block)

Why can't you just do?

F: Send + core::future::Future + 'static
F::Output: Send + core::future::Future + 'static

What function are you passing it to?

You can create a function that returns your transmuted data with an extended lifetime. I think it will be inlined by the compiler, so there won't be any overhead.

Example:

unsafe fn extend_lifetime<'input_lifetime, F>(r: &'input_lifetime F) -> &'static F {
    std::mem::transmute::<&'input_lifetime F, &'static F>(r)
}

fn demo<I: std::fmt::Display + 'static>(i: &'static I) {
    println!("{}", *i)
}

fn main() {
    unsafe {
        demo(extend_lifetime(&123))
    }
}

Playground.

It is what I have right now.
And I pass it to tokio::runtime::Runtime::spawn

The problem is that if I pass future that gets created from <MyObject>::async_method() where async method borrows object, it will extend lifetime of borrow to static.
But I specifically want to spawn and await for completetion without extending lifetime (I do so with pseudo condvar)

Easiest solution would be to use block_on but then I would need to prevent concurrent calls to it...
Which I think now might be easier rather than hacking this 'static bug

P.s. as this is C library, I have to make Runtime static

This sadly adds Unpin requirement as any reference to Future require it and unchecked Pin doesn't help much as it still requires static (I tried static reference already :slight_smile:)

For now my best attempt was this:

pub fn block_on<F: Send + core::future::Future + Sized, F2: core::future::Future<Output=F::Output> + Send + 'static + Sized>(fut: F) -> F::Output where F::Output: Send {

    let new_fut: WaitFuture<F2> = WaitFuture(unsafe { core::mem::transmute_copy(&fut) }, result_ptr, signal);
    core::mem::forget(fut);

In this case do you think you could desugar the async method to this,

impl MyObject {
    fn async_method(&self, args: Args) -> impl Future<Output = ???> + 'static {
        async move {
            // here move what you need into the future, without extending the lifetime of self
        }
    }
}

I wish it would be so simple... but that would require boxing parts of self which I'd prefer to avoid as this is just work-around for compiler bugs (there is no actual need for that)

Worst case I'll just have to make API more ugly to not borrow at all...

There are three ways of assigning a 'static lifetime:

  • Declaring a variable with the static keyword
  • Allocating and memory on the heap
  • Closures, that don't capture variables by reference (use the move keyword) and function pointers

Not even

fn main() {
    let a: usize = 1;
    let b: &'static usize = &a;
}

works. You'll get an error message, that tells you "a does not live long enough", because it is dropped at the end of main while b is still borrowed, because the end of main is technically not the end of program.

You can do something like

fn main() {
    static A: usize = 1;
    let b: &'static usize = &A;
}

, but that only works, if the value you assign is a constant expression, which is probably not what you want.

If you can pass your F-type variable by value, then you may be able to work with a closure:

let outer_future = /* [...] */;
some_function(move || {
    let inner_future: F = outer_future;

    // [...]

    return some_other_future;
});

If that doesn't work for you, please provide information on what the function actually takes as a parameter.

To put it simply I have something like that

If you remove 'static then compiler would just require 'static lifetime on F

wait looks a lot like block_on, why not use that directly?

block_on doesn't allow concurrent calls in 0.2 so I would need to use some sort of additional lock

async-std provides a different implementation of block_on if you can't use it directly, then at least you can model this off of async-std's implementation.

This is giving me a headache. You cannot consume a static value, i.e. self must be a reference and in conclusion you have to implement Future for the reference of one of your types and that type must be static, because functions and closures cannot implement traits.

You'd need something like this:

// It doesn't make sense to implement it for &mut MyType, because you cannot safely mutate static values.
impl Future for &MyType {
    // [...]
}

static MY_TYPE: MyType = /* [...] */;

fn some_function() {
    let result = MY_TYPE.wait();
    // [...]
}

I can't imagine that this is what you want, though. It just looks so weird to me.

yeah, this is insane thing.
There is probably no way to hack it with current compiler.

Only way would be to re-think my blocking approach, I do not wanna make any 'static requirements for blocking wait

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.