4

I have read through the first half of the book's second edition as well as this chapter in the first edition. I am still confused how to initialize static variables.

In the end I'd like to have a function local static HashSet<char> containing all digit characters.

Attempt 1:

fn is_digit(c: char) -> bool {
    static set: std::collections::HashSet<char> =
        ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']
            .iter()
            .cloned()
            .collect();

    return set.contains(&c);
}

The compiler yields:

error[E0015]: calls in statics are limited to struct and enum constructors
 --> src/main.rs:3:9
  |
3 | /         ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']
4 | |             .iter()
  | |___________________^
  |
note: a limited form of compile-time function evaluation is available on a nightly compiler via `const fn`
 --> src/main.rs:3:9
  |
3 | /         ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']
4 | |             .iter()
  | |___________________^

Attempt 2: (No data, only construction)

static set: std::collections::HashSet<char> = std::collections::HashSet::new();

The compiler yields:

error[E0015]: calls in statics are limited to struct and enum constructors
 --> src/main.rs:1:47
  |
1 | static set: std::collections::HashSet<char> = std::collections::HashSet::new();
  |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |

This on the other hand works:

let set: std::collections::HashSet<char> = std::collections::HashSet::new();

HashSet is a struct.

That is why I don't understand the error from attempt 2. I am trying to call the struct's constructor and the compiler says I can only call the constructor of a struct or enum.

I guess new() is not a constructor call after all...?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Noel Widmer
  • 4,444
  • 9
  • 45
  • 69
  • Related: https://stackoverflow.com/questions/27582739/how-do-i-create-a-hashmap-literal and https://stackoverflow.com/questions/28392008/more-concise-hashmap-initialization – Boiethios Jun 27 '17 at 09:40
  • @Boiethios These are (more) convenient ways to initialize `HashSet`s right? But they don't solve the static issue I believe... – Noel Widmer Jun 27 '17 at 09:46
  • This is the same as a "global" singleton, just put it inside the function that cares about it. – Shepmaster Jun 27 '17 at 15:56

1 Answers1

2

To use a static variable like in other languages (for example C++), you can use this crate. It does lazy initialization to simulate this behavior.

But IMO, in your example, such a feature is overkill. You can simply do this:

fn is_digit(c: char) -> bool {
    match c {
        '0'...'9' => true,
        _ => false,
    }
}

fn main() {
    assert_eq!(is_digit('0'), true);
    assert_eq!(is_digit('5'), true);
    assert_eq!(is_digit('9'), true);
    assert_eq!(is_digit('a'), false);
}

Or even better, use the standard:

fn is_digit(c: char) -> bool {
    c.is_digit(10)
}

About the struct, you are right: constructors do not exist in Rust. The compiler speaks about enum constructors that are different from constructors in other object languages. The best for you is to continue reading the book if you want more information.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Boiethios
  • 38,438
  • 19
  • 134
  • 183
  • I will benchmark the time complexity for the `match` vs the `HashSet` and take a look int the *lazy* crate. – Noel Widmer Jun 27 '17 at 10:29
  • 3
    @NoelWidmer: Unless the optimizer is *really*, *really*, good, I expect `match` to beat `HashSet` hands down. `match` will compile down to a couple CPU instructions (it's just a range check) whereas the other requires (1) computing a hash, (2) doing a table look-up with some collision-handling logic, (3) while chasing some pointers around, ... even if you managed to get the `HashSet` initialized at compile-time, I'd still not expect the optimizer to manage to catch to `match` performance. – Matthieu M. Jun 27 '17 at 12:08