英文:
require an ordered vector for struct initialization
问题
我有一个结构体:
pub struct OneDLookup<T: PartialOrd + Sub + Add + Copy + Clone, U: Add + Sub + Copy + Clone>{
breakpoints: Vec<T>,
values: Vec<U>,
last_diff_bp: f64, //忽略这些,它们对这个问题不重要
last_diff_values: f64,
first_diff_bp: f64,
first_diff_values: f64,
}
这将用作查找表,但这个结构体必须满足两个重要条件:
- breakpoints 必须按升序排列,例如1、5、7、9,不能是其他顺序。
- breakpoints 和 values 必须具有相同的长度。
是否可能在编译时检查这些条件(或者在编写过程中使用 rust-analyzer 更好)?我担心可能不行。
我对 Rust 还很陌生,仍在学习其中的细节。
我尝试创建了一个小宏,基于 vec!()
宏,以查看我是否能够理解它是如何工作的,但不幸的是,我一点也不明白:
macro_rules! create_1d_lookup {
(($($bps:expr,)*); ($($vals:expr,)*)) => (
$crate::my_crate::OneDLookup::new(vec![$($bps),+], vec![$($vals),+])
);
}
OneDLookup::new() 接受 breakpoints 和 values 向量作为参数。
这个结构体在初始化后不会改变。
英文:
I have a struct:
pub struct OneDLookup <T: PartialOrd + Sub + Add + Copy + Clone, U: Add + Sub + Copy + Clone>{
breakpoints: Vec<T>,
values: Vec<U>,
last_diff_bp: f64, //ignore these, they are not important to this question
last_diff_values: f64,
first_diff_bp: f64,
first_diff_values: f64,
}
This will function as a lookup table, however there are 2 important things that have to be true about this struct:
breakpoints must be in ascending order 1,5,7,9 for example, and not any other order.
breakpoints and values must have the same length.
Is it possible to check these at compile time (or even better with the rust-analyzer during writing) in a macro? I fear it might not be.
I'm quite new to rust so I'm still learning the ins and outs.
I tried to make a tiny macro that doesn't work based on the vec!()
macro to see if I could understand what was done there but unfortunately I have no idea:
macro_rules! create_1d_lookup {
(($($bps:expr,)*); ($($vals:expr,)*)) => (
$crate::my_crate::OneDLookup::new(vec![$($bps),+], vec![$($vals),+])
);
}
OneDLookup::new() takes the breakpoints and values vectors as arguments.
This struct is constant and will not change after initialization.
答案1
得分: 2
您可以通过创建一个const
来执行一些逻辑来验证不变量是否得以保持,并且如果它们被破坏,就会引发恐慌。恐慌将被转换为编译失败:
macro_rules! create_1d_lookup {
(($($bps:expr,)*); ($($vals:expr,)*)) => {{
const _: () = {
let breakpoints = [ $($bps,)* ];
if breakpoints.len() != [ $($vals,)* ].len() {
// 有多种在宏中计数的方法,
// 假设这些表达式没有副作用,这是最简单的方法。
panic!("向量的大小不匹配");
}
let mut i = 1;
// 当前无法在const中使用`for`循环。
while i < breakpoints.len() {
if breakpoints[i - 1] > breakpoints[i] {
panic!("断点未排序");
}
i += 1;
}
};
$crate::my_crate::OneDLookup::new(vec![$($bps),+], vec![$($vals),+])
}};
}
不幸的是,这是一种后单态化错误,这意味着它不会在cargo check
中显示,只会在cargo build
中显示。
英文:
You can do that by creating a const
that runs some logic to verify the invariants are upheld and panics if they're broken. The panic will be translated into a compilation failure:
macro_rules! create_1d_lookup {
(($($bps:expr,)*); ($($vals:expr,)*)) => {{
const _: () = {
let breakpoints = [ $($bps,)* ];
if breakpoints.len() != [ $($vals,)* ].len() {
// There are multiple ways to count in macros,
// this is the simplest assuming the expressions are side-effect-free.
panic!("sizes of vectors don't match");
}
let mut i = 1;
// Can't use a `for` loop in consts currently.
while i < breakpoints.len() {
if breakpoints[i - 1] > breakpoints[i] {
panic!("breakpoints aren't sorted");
}
i += 1;
}
};
$crate::my_crate::OneDLookup::new(vec![$($bps),+], vec![$($vals),+])
}};
}
Unfortunately, this is a post-monomorphization error, meaning it won't show up in cargo check
, only in cargo build
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论