Code compiles in 1.78.0, but not in 1.79.0

pub static ASSETS_DIR: Option<include_dir::Dir<'_>> = if cfg!(target_arch = "wasm32") {
    Some(include_dir::include_dir!("$CARGO_MANIFEST_DIR/assets"))
} else {
    None
};

This code compiles in older versions of Rust, but after updating to 1.79.0 I get this error:

error[E0716]: temporary value dropped while borrowed
 --> src/main.rs:2:10
  |
2 |     Some(include_dir::include_dir!("$CARGO_MANIFEST_DIR/assets"))
  |     -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
  |     |    |
  |     |    creates a temporary value which is freed while still in use
  |     using this value as a static requires that borrow lasts for `'static`
3 | } else {
  | - temporary value is freed at the end of this statement
  |
  = note: this error originates in the macro `include_dir::include_dir` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0716`.

I imagine the issue is related to the if statement because this compiles without any issues in 1.79.0:

pub static ASSETS_DIR: Option<include_dir::Dir<'_>> = Some(include_dir::include_dir!("$CARGO_MANIFEST_DIR/assets"));

This is using include_dir version 0.7.3 and the macro looks something like this when inlined:

    Some(include_dir::Dir::new(
        "",
        &[include_dir::DirEntry::File(include_dir::File::new(
            "test_file.txt",
            b"",
        ))],
    ))

Any help would be greatly appreciated.

2 Likes

This change maybe. Though they didn't seem to think it would break things.

You should definitely file a regression report.

1 Like

Strangely enough, #121346 looks like it should've made this start successfully compiling, not stop, although AIUI it shouldn't've compiled successfully before nor after the change... outside of a constant initializer context, anyway; IIRC such contexts change how promotion works and are supposed to promote to &'static instead of &'let.

Minimal reproduction:

pub struct Dir<'a>(&'a [File<'a>]);
impl<'a> Dir<'a> {
    pub const fn new(x: &'a [File<'a>]) -> Self {
        Self(x)
    }
}

pub struct File<'a>(&'a str);
impl<'a> File<'a> {
    pub const fn new(x: &'a str) -> Self {
        Self(x)
    }
}

pub static OK: Dir<'static> = Dir::new(&[File::new("")]);
pub static ERR: Dir<'static> = if true {
    Dir::new(&[File::new("")])
} else {
    unreachable!()
};

The workaround for specifically the OP will be to use #[cfg] instead of if cfg!:

#[cfg(target_arch = "wasm32")]
pub static ASSETS_DIR: Option<include_dir::Dir<'_>> = Some(include_dir::include_dir!("$CARGO_MANIFEST_DIR/assets"));
#[cfg(not(target_arch = "wasm32"))]
pub static ASSETS_DIR: Option<include_dir::Dir<'_>> = None;

This is probably what you would prefer anyway, since this means include_dir! won't happen for non-wasm builds, whereas just using if cfg! still expands the include_dir! macro.


Since I went ahead and did the initial look into what's going on, I went ahead and filed a regression issue.

9 Likes

It looks like this is #121557 instead, which deliberately restricts promotion of const fn calls. That also indicates another potential successful rewrite:

pub static ASSETS_DIR: Option<include_dir::Dir<'_>> = {
    let it = include_dir::include_dir!("$CARGO_MANIFEST_DIR/assets");
    if cfg!(target_arch = "wasm32") {
        Some(it)
    } else {
        None
    }
}
3 Likes

The intended rewrite is to change this

    Some(include_dir::Dir::new(
        "",
        &[include_dir::DirEntry::File(include_dir::File::new(
            "test_file.txt",
            b"",
        ))],
    ))

to this

    Some(include_dir::Dir::new(
        "",
        const { &[include_dir::DirEntry::File(include_dir::File::new(
            "test_file.txt",
            b"",
        ))] },
    ))

If you want something to be const-promoted, then it's much better to just tell the compiler explicitly. Having the compiler implicitly promote function calls is problematic for the reasons explained in this RFC.

3 Likes

For this specific case, I'd agree that two separate #[cfg(..)]'d declarations (or perahps cfg_if) is probably better (since it won't even evaluate the include_dir! when cfg'd out).

Note that const { ... } expressions are not available on Rust <= 1.79.0 (and you can't rewrite the output of the include_dir! macro yourself to include one anyway).

If others have a similar issue which is not adequately solved by the #[cfg(..)] solution (e.g. if the conditional is an more complicated expression based on not just a cfg!), a workaround that works on versions 1.78.0 and 1.79.0 is to put the include_dir invocation unconditionally into a separate const item which will be promoted/checked separately, e.g.

pub static ASSETS_DIR: Option<Dir<'static>> = if cfg!(target_arch = "wasm32") {
  const ASSETS_DIR_IMPL: Dir<'static> = include_dir::include_dir!("$CARGO_MANIFEST_DIR/assets");
  Some(ASSETS_DIR_IMPL)
} else {
  None
};
2 Likes

Yeah, that's the backwards-compatible way, but unfortunately it still relies on implicit promotion of File::new -- something rustc should never have accepted and something we might try to phase out eventually, over an edition or so.

I opened an issue against include_dir so hopefully this can be fixed properly in the crate.

1 Like

Given that the last commit to that repo was 2 years ago, I'm doubtful anything will happen. Looking at open and closed issues I also don't think anything will happen.

The author is active on this forum though, so I'd take the liberty to just сс @Michael-F-Bryan and hope for the answer from them directly.

3 Likes

Fixed.

19 Likes

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.