Skip to content

Casting away lifetimes using closures #65700

Open
@alecmocatta

Description

@alecmocatta
Contributor

The following code compiles on stable and nightly. I'm not sure it should as it's effectively casting a Struct::<'a> to a Struct::<'static> in safe code.

use std::{any::Any, marker::PhantomData};

fn main() {
    bad();
}

fn bad<'a>() -> Struct<'static> {
    make_static(|| Struct::<'a>(PhantomData))
}

struct Struct<'a>(PhantomData<&'a ()>);

fn make_static<A, B>(a: A) -> B
where
    A: FnOnce0 + 'static,
    B: 'static,
{
    let boxed: Box<dyn Any + 'static> = Box::new(a.call());
    *Box::<dyn Any + 'static>::downcast(boxed).unwrap()
}

trait FnOnce0 {
    type Output;
    fn call(self) -> Self::Output;
}

impl<F, O> FnOnce0 for F
where
    F: FnOnce() -> O,
{
    type Output = F::Output;
    fn call(self) -> Self::Output {
        self()
    }
}

(Playground)

It seems lifetime and type parameters that are used in the closure don't affect the lifetime of the closure itself. I stumbled upon this after noticing that || T::default() is 'static whereas T::default() is not.

Activity

jonas-schievink

jonas-schievink commented on Oct 22, 2019

@jonas-schievink
Contributor

It seems lifetime and type parameters that are used in the closure don't affect the lifetime of the closure itself.

Should they, though? The lifetime of a closure is determined by the captured environment it contains.

Also note that it fails to compile when you try to explicitly specify 'a (citing late-bound lifetimes) or when you try to pass in the non-'static struct as an argument to bad. While I don't know the specific rules here (especially about when 'a is treated as late-bound), this doesn't look unsound to me.

alecmocatta

alecmocatta commented on Oct 22, 2019

@alecmocatta
ContributorAuthor

Thanks @jonas-schievink. As far as I'm aware it doesn't enable unsoundness. However the conversion from Struct<'a> to a Struct<'static> was surprising to me, so I thought I'd seek a second opinion.

matthewjasper

matthewjasper commented on Oct 22, 2019

@matthewjasper
Contributor

I tried fixing this in #60332, but it ends up breaking too much code.

added
A-closuresArea: Closures (`|…| { … }`)
A-lifetimesArea: Lifetimes / regions
C-bugCategory: This is a bug.
T-langRelevant to the language team
on Oct 22, 2019
Spoonbender

Spoonbender commented on Oct 17, 2022

@Spoonbender

triage: no change

(code still compiles)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-closuresArea: Closures (`|…| { … }`)A-lifetimesArea: Lifetimes / regionsC-bugCategory: This is a bug.T-langRelevant to the language team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @alecmocatta@jonas-schievink@Spoonbender@matthewjasper

        Issue actions

          Casting away lifetimes using closures · Issue #65700 · rust-lang/rust