让我们考虑这样一个函数:
fn test<const N: usize>() -> [f64; N] {
if N == 1 {
[0.0_f64; 1]
} else if N == 2 {
[1.0_f64; 2]
} else {
panic!()
}
}
Run Code Online (Sandbox Code Playgroud)
N我的理解是编译器会在编译时评估 的值。如果是这种情况,该if语句也可以在编译时求值,因此应该返回正确的类型,因为[0.0_f64; 1]仅返回 ifN == 1且[1.0_f64; 2]仅返回 if N == 2。
现在,当我尝试编译此代码时,编译器失败,基本上告诉我返回数组的尺寸是错误的,因为它们没有明确地具有N长度。
我确实意识到,我可以将这个具体示例实现为
fn test<const N: usize>() -> [f64; N] {
match N {
1 => { [0.0_f64; N] },
2 => { [1.0_f64; N] },
_ => { panic!("Invalid value {}", N) },
}
}
Run Code Online (Sandbox Code Playgroud)
但这在我的实际代码中不起作用,因为它针对不同的分支使用具有固定数组大小的不同函数。
有办法做到这一点吗?也许使用像#![cfg]万客罗这样的东西?
为了澄清为什么我的问题不起作用,让我们写下来:
fn some_fct() -> [f64; 1] {
[0.0_f64; 1]
}
fn some_other_fct() -> [f64; 2] {
[1.0_f64; 2]
}
fn test<const N: usize>() -> [f64; N] {
match N {
1 => some_fct(),
2 => some_other_fct(),
_ => {
panic!("Invalid value {}", N)
}
}
}
Run Code Online (Sandbox Code Playgroud)
由于程序结构中的其他限制,我无法真正以通用大小编写some_fct()和返回。some_other_fct()
您可以使用通用特征来做到这一点:
trait Test<const N: usize> {
fn test() -> [f64; N];
}
Run Code Online (Sandbox Code Playgroud)
然后将其实现为零大小的类型:
struct T;
impl Test<1> for T {
fn test() -> [f64; 1] {
return [0.0_f64; 1];
}
}
impl Test<2> for T {
fn test() -> [f64; 2] {
return [1.0_f64; 2];
}
}
Run Code Online (Sandbox Code Playgroud)
缺点是调用起来有点麻烦:
fn main() {
dbg!(<T as Test<1>>::test());
dbg!(<T as Test<2>>::test());
}
Run Code Online (Sandbox Code Playgroud)
但正如@eggyal 下面评论的那样,您可以添加一个具有编写良好的绑定的通用函数来获取所需的语法:
fn test<const N: usize>() -> [f64; N]
where
T: Test<N>
{
T::test()
}
fn main() {
dbg!(test::<1>());
dbg!(test::<2>());
}
Run Code Online (Sandbox Code Playgroud)
现在,你就没有了“用panic!错N了”的行为。考虑这是一个功能而不是限制:如果您使用错误,N您的代码将无法编译,而不是在运行时出现恐慌。
如果你真的想要这种panic!()行为,你可以使用 的不稳定功能来获得它#![feature(specialization)],只需添加default到这个实现中:
impl<const N: usize> Test<N> for T {
default fn test() -> [f64; N] {
panic!();
}
}
Run Code Online (Sandbox Code Playgroud)
但该功能被明确标记为不完整,所以我还不会指望它。
这是一个不是特别聪明的解决方案,但很容易理解并且类似于原始解决方案:
fn test<const N: usize>() -> [f64; N] {
match N {
1 => some_fct().as_slice().try_into().unwrap(),
2 => some_other_fct().as_slice().try_into().unwrap(),
_ => {
panic!("Invalid value {}", N)
}
}
}
Run Code Online (Sandbox Code Playgroud)
尽管代码看起来像是在运行时检查数组大小,但godbolt表明 rustc/LLVM 能够推理出[f64; N].as_slice().try_into()始终成功地将数组转换的切片强制转换为[f64; N]. 生成的代码test<1>因此test<2>不包含任何检查或恐慌,并且test<N>由于N>2全能匹配臂中的恐慌而无条件地发生恐慌。
| 归档时间: |
|
| 查看次数: |
720 次 |
| 最近记录: |