Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a static integer in the definition of a struct

Tags:

generics

rust

I'm trying to migrate some C++ code to Rust. I have tried lots of different approaches but none of them compile.

I want a generic template that can handle different types and has a adjustable total size with a static field (const expression) Capacity:

template<class KeyType, class ValueType, int PageSize>
struct BplusTreeLeaf {
    static const uint16_t Capacity = (PageSize-16)/(sizeof(KeyType)+sizeof(ValueType));
    KeyType keys[Capacity];
    ValueType values[Capacity];
};

I want to access the capacity from outside:

for(int i = 0; i < BplusTreeLeaf<x, y, 4096>::Capacity; i ++) { ... }

It seems that there is no way to do something like this in Rust, or at least in my understanding of Rust:

  • static is not allowed in a struct and the documentation tells me to use macros
  • only types can be "templated" in Rust but not values or expressions. I can't even pass the total size as an argument to the struct definition

This is as far as I got:

macro_rules! BplusTreeLeaf {
    ($KeyType:ident, $ValueType:ident, $PageSize:expr) => {
        static Capacity_: u16 = ($PageSize - 16) / (std::mem::size_of::<$KeyType>() + std::mem::size_of::<$ValueType>());
        struct BplusTreeLeaf_ {
            keys: [$KeyType, ..Capacity_],
            values: [$ValueType, ..Capacity_],
        }
    }
}

BplusTreeLeaf!(u64, u64, 4096)

The compiler yields "expected constant expr for vector length" which is incorrect because I did not use "mut" for Capacity_, so it has to be a const expression. Even if it would work, Capacity_ and BplusTreeLeaf_ would still be in the global scope/namespace.

Have I misunderstood something elementary in Rust's design or is it just impossible? If it isn't possible now, is there something planned as a future feature or should I stay with C++ 11?

like image 771
Alexander Meißner Avatar asked Aug 30 '14 17:08

Alexander Meißner


People also ask

Can we use static variable in structure?

6) Static variables should not be declared inside structure. The reason is C compiler requires the entire structure elements to be placed together (i.e.) memory allocation for structure members should be contiguous.

What does static int mean in C?

static int is a variable storing integer values which is declared static. If we declare a variable as static, it exists till the end of the program once initialized.

Why static members are defined outside the class?

The reason for this is simple, static members are only declared in a class declaration, not defined. They must be explicitly defined outside the class using the scope resolution operator. If we try to access static member 'a' without an explicit definition of it, we will get a compilation error.


1 Answers

The primary feature you are looking for is called const generics. Basic support is available in Rust 1.51:

struct BplusTreeLeaf<K, V, const CAP: usize> {
    keys: [K; CAP],
    values: [V; CAP],
}

impl<K, V, const CAP: usize> BplusTreeLeaf<K, V, CAP> {
    const CAPACITY: usize = CAP;
}

fn main() {
    println!("{}", BplusTreeLeaf::<u8, f32, 16>::CAPACITY);
    println!("{}", BplusTreeLeaf::<i32, bool, 32>::CAPACITY);
}

As you mentioned, you can create a macro for previous versions of Rust which will create one-off types with a specific capacity:

macro_rules! make_leaf {
    ($name:ident, $capacity:expr) => {
        struct $name<K, V> {
            keys: [K; $capacity],
            values: [V; $capacity],
        }
        
        impl<K, V> $name<K, V> {
            const CAPACITY: usize = $capacity;
        }
    }
}

make_leaf!(BplusTreeLeaf16, 16);
make_leaf!(BplusTreeLeaf32, 32);

fn main() {
    println!("{}", BplusTreeLeaf16::<u8, f32>::CAPACITY);
    println!("{}", BplusTreeLeaf32::<i32, bool>::CAPACITY);
}

See also:

  • Is it possible to control the size of an array using the type parameter of a generic?
like image 90
Shepmaster Avatar answered Nov 15 '22 11:11

Shepmaster