Ivan Temchenko
Ivan Temchenko

Reputation: 844

Internal compiler error when creating a static constant

I'm trying to create static or constant from matching the env::home_dir:

static home: String = match env::home_dir() {
    Some(ref p) => p.to_str().unwrap().to_owned(),
    None => "./".to_string(),
};

But I'm getting a compiler bug:

src/main.rs:15:8: 15:13 error: internal compiler error: no enclosing scope found for scope: CodeExtent(346/Misc(20))
src/main.rs:15          Some(ref p) => p.to_str().unwrap().to_owned(),
                             ^~~~~
note: the compiler unexpectedly panicked. this is a bug.
note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports
note: run with `RUST_BACKTRACE=1` for a backtrace
thread 'rustc' panicked at 'Box<Any>', ../src/libsyntax/diagnostic.rs:175

Is this a bug in my code or I should report this bug? Is it possible to get home as constant or static value?

I need to use it multiple times to add some file names for some file operations and I want to have them all in same folder.

Upvotes: 2

Views: 164

Answers (2)

Shepmaster
Shepmaster

Reputation: 430358

Is this a bug in my code

Your code has something that doesn't make sense, so I guess it's a bug.

should I report this bug?

Probably. A compiler shouldn't crash, it should report the error nicely to the user. Look to see if there is an existing bug first though, and comment on it if there is.

Is it possible to get home as constant or static value?

Not exactly. Constant values are required to live for the entire length of the program. That means they exist before main starts and after main exits. It is impossible for the call to env::home_dir() to meet that exact criteria.

The most straight-forward thing to do is to get the value once and then pass it where it is needed. This allows you to avoid getting the value multiple times:

fn main() {
    let home = env::home_dir();
    let home = home.as_ref().map(|d| d.as_path()).unwrap_or(Path::new("./"));

    function_1(&home);
    function_2(&home);
    function_3(&home);
}

You could also place it into a struct, either as a &Path or a PathBuf:

struct Thing {
    home: PathBuf,
}

fn main() {
    let home = env::home_dir().unwrap_or_else(|| PathBuf::from("./"));
    let t = Thing { home: home };

    t.function_1();
    t.function_2();
    t.function_3();
}

This is probably what I'd do. There's no reason to keep that memory allocated forever, as a static value would.

I need to use it multiple times to add some file names for some file operations and I want to have them all in same folder.

There's nothing preventing you from simply creating a function to place the logic in:

fn safe_home_dir() -> PathBuf {
    env::home_dir().unwrap_or_else(|| PathBuf::from("./"))
} 

Then just call the function multiple times. You could profile to see how expensive it was, but my gut feeling is that there isn't going to be appreciable slowness here.


If you really needed it to be static, then you can use the lazy_static crate.

Upvotes: 4

XAMPPRocky
XAMPPRocky

Reputation: 3599

Answer

Static variables can't have logic, to do this you have to use the lazy_static crate, which enables you to do logic. A small note, in order to access the veriable you have to deference it. so println!("{}", HOME); doesn't work, but println!("{}", *HOME); does.

Solution

#[macro_use]
extern crate lazy_static;
use std::env;
lazy_static! {
    static ref HOME: String = {
        match env::home_dir() {
            Some(p) => p.to_str().unwrap().to_owned(),
            None => "./".to_string(),
        }
    };
}

fn main() {
    println!("{:?}", *HOME);
}

Upvotes: 2

Related Questions