【问题标题】:Using a static integer in the definition of a struct在结构定义中使用静态整数
【发布时间】:2014-10-24 11:08:02
【问题描述】:

我正在尝试将一些 C++ 代码迁移到 Rust。我尝试了很多不同的方法,但没有一个可以编译。

我想要一个通用模板,它可以处理不同的类型,并且具有可调整的总大小和静态字段(常量表达式)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];
};

我想从外部访问容量:

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

似乎在Rust中没有办法做这样的事情,或者至少在我对Rust的理解中:

  • static 不允许在结构中使用,文档告诉我使用宏
  • 在 Rust 中,只有类型可以“模板化”,而值或表达式则不能。我什至不能将总大小作为参数传递给结构定义

据我所知:

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)

编译器产生“向量长度的预期常量 expr”,这是不正确的,因为我没有对 Capacity_ 使用“mut”,所以它必须是一个 const 表达式。即使它可以工作,Capacity_BplusTreeLeaf_ 仍然会在全局范围/命名空间中。

我是否误解了 Rust 设计中的一些基本内容,或者这是不可能的?如果现在不可能,是否有计划作为未来的功能,还是我应该继续使用 C++ 11?

【问题讨论】:

  • 是的,很多未实现的功能。编译器给出“expected constant expr”错误,因为 mem::size_of 是一个普通函数,它不是“constexpr”。 AFAIK,他们将在 1.0 之后的某个地方添加“constexpr”和“非类型模板参数”,并在 1.0 之前添加“静态成员”,使用此 RFC:github.com/rust-lang/rfcs/pull/195
  • 我想我必须等待 :( 但是感谢 GitHub 问题的链接

标签: generics rust


【解决方案1】:

这个问题很老,但仍然有效。你可以使用lazy_static crate。

普通静态变量必须用常量表达式初始化,其中可能包括常量函数。这允许它们在编译时被评估,并被烘焙到二进制文件中。但是,这会带来许多限制,例如没有堆分配。

您想在程序第一次运行时评估常量。像这个例子一样,直接来自 crate 的页面:

lazy_static! {
    static ref HASHMAP: HashMap<u32, &'static str> = {
        let mut m = HashMap::new();
        m.insert(0, "foo");
        m.insert(1, "bar");
        m.insert(2, "baz");
        m
    };
}

fn main() {
    // First access to `HASHMAP` initializes it
    println!("The entry for `0` is \"{}\".", HASHMAP.get(&0).unwrap());

    // Any further access to `HASHMAP` just returns the computed value
    println!("The entry for `1` is \"{}\".", HASHMAP.get(&1).unwrap());
}

【讨论】:

  • 您误解了 OP 想要什么。他们正在寻找const generics
  • 哦,好吧,那是我的错。值得删除答案,还是我应该留下它?
  • 你可以改变它来展示如何使用宏和相关的常量来实现它。后面可能是一个假设的例子,说明 RFC 实施后会是什么样子。
  • 好的,我很快就会想出一些东西。
【解决方案2】:

您正在寻找的主要功能称为const generics。 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);
}

正如您所提到的,您可以为早期版本的 Rust 创建一个宏,该宏将创建具有特定容量的一次性类型:

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);
}

另见:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 2011-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-08
    相关资源
    最近更新 更多