#const #loops #const-fn #for #range-iterator

build const_for

使用宏实现的 const 上下文中的循环

5 个版本

0.1.4 2024年1月22日
0.1.3 2023年11月4日
0.1.2 2023年3月19日
0.1.1 2023年3月10日
0.1.0 2023年3月10日

#66 in 构建实用工具

Download history 518/week @ 2024-03-13 930/week @ 2024-03-20 688/week @ 2024-03-27 612/week @ 2024-04-03 545/week @ 2024-04-10 1061/week @ 2024-04-17 694/week @ 2024-04-24 389/week @ 2024-05-01 672/week @ 2024-05-08 578/week @ 2024-05-15 519/week @ 2024-05-22 1413/week @ 2024-05-29 1297/week @ 2024-06-05 1311/week @ 2024-06-12 2448/week @ 2024-06-19 3142/week @ 2024-06-26

8,595 每月下载量
用于 5 个 crate (2 直接)

MIT 许可证

17KB
55

const_for

Const for

GitHub crates.io Docs

常规循环在 const 上下文中不允许,因为它依赖于迭代器,而在 const 中不可用。
在编写 const 函数时,这相当烦人,因为需要使用 'loop' 或 'while' 编写自定义循环。

此 crate 提供了一个宏实现,可以在 const 上下文中使用,用于遍历范围。
目标是尽可能模仿常规循环。它正确处理 break 和 continue,并且在循环体中变量不可变。
为了使循环尽可能灵活,它附带宏变体来处理 .rev() 和 step_by(x),这模仿了相应的函数调用。这是必要的,因为通常它们依赖于非 const 迭代器。但在这里可以使用相同的语法使用它们。

主要的限制是,该宏仅支持标准(排他性)范围,例如 0..10 和 -5..5,但不支持 ..5 或 0..=10。这主要是当前稳定 Rust 的限制,在没有使用 #![feature(const_range_bounds)] 成为稳定版之前不可能实现。

let mut a = 0;
const_for!(i in 0..5 => {
    a += i
});
assert!(a == 10)

这与以下常规循环等效,但可以在 const 上下文中使用。

let mut a = 0;
for i in 0..5 {
    a += i
}
assert!(a == 10)

自定义步长

可以设置自定义步长

let mut v = Vec::new();
const_for!(i in (0..5).step_by(2) => {
    v.push(i)
});
assert!(v == vec![0, 2, 4])

循环的行为就像函数被调用在范围上,但它是由宏实现的。
它与以下非 const 循环等效

let mut v = Vec::new();
for i in (0..5).step_by(2) {
    v.push(i)
}
assert!(v == vec![0, 2, 4])

反转

可以反转迭代

let mut v = Vec::new();
const_for!(i in (0..5).rev() => {
    v.push(i)
});
assert!(v == vec![4, 3, 2, 1, 0])

循环的行为就像函数被调用在范围上,但它是由宏实现的。
它与以下非 const 循环等效

let mut v = Vec::new();
for i in (0..5).rev() {
    v.push(i)
}
assert!(v == vec![4, 3, 2, 1, 0])

反转和自定义步长

可以组合 rev 和 step_by,但每个只能附加一次。所以以下两个示例是唯一合法的组合。

// Reverse, then change step size
let mut v = Vec::new();
const_for!(i in (0..10).rev().step_by(4) => {
    v.push(i)
});
assert!(v == vec![9, 5, 1]);

// Change step size, then reverse
let mut v = Vec::new();
const_for!(i in (0..10).step_by(4).rev() => {
    v.push(i)
});
assert!(v == vec![8, 4, 0])

说明

可以使用可变和通配符变量作为循环变量,并且它们的行为符合预期。

// Mutable variable
let mut v = Vec::new();
const_for!(mut i in (0..4) => {
    i *= 2;
    v.push(i)
});
assert!(v == vec![0, 2, 4, 6]);

// Wildcard variable
let mut a = 0;
const_for!(_ in 0..5 =>
   a += 1
);
assert!(a == 5)

循环体可以是任何语句。这意味着以下也是合法的,即使它不在常规循环中。

let mut a = 0;
const_for!(_ in 0..5 => a += 1);

unsafe fn unsafe_function() {}
const_for!(_ in 0..5 => unsafe {
   unsafe_function()
});

如果范围的开始加上步长溢出整数,行为是未定义的。

实际示例

以下是一个示例,说明此 crate 如何帮助使某些实际代码更简洁、更易于阅读。

代码是从 Cadabra 棋盘引擎中提取的(并稍作编辑以增强清晰度)。

之前

const fn gen_white_pawn_attacks() -> [u64; 64] {
    let mut masks = [0; 64];

    let mut rank: u8 = 0;
    while rank < 8 {
        let mut file: u8 = 0;
        while file < 8 {
            let index = (rank*8+file) as usize;
            if file != 7 { masks[index] |= (1 << index) >> 7 as u64 }
            if file != 0 { masks[index] |= (1 << index) >> 9 as u64 }

            file += 1;
        }
        rank += 1;
    }

    masks
}

之后

const fn gen_white_pawn_attacks() -> [u64; 64] {
    let mut masks = [0; 64];

    const_for!(rank in 0..8 => {
        const_for!(file in 0..8 => {
            let index = (rank*8+file) as usize;
            if file != 7 { masks[index] |= (1 << index) >> 7 as u64 }
            if file != 0 { masks[index] |= (1 << index) >> 9 as u64 }
        })
    });

    masks
}

许可证:MIT

无运行时依赖