Initialize array of bitset

My tiny chess engine contains a few fixed size arrays where each element is a bitset. I was not able to find a way to initialize it. To fix

use bit_set::BitSet;

type A = [BitSet<usize>; 4];

fn main() {
    println!("f");
    let mut a: A;
    a[0].insert(3);
}

GPT-4 suggests

use bit_set::BitSet;

type A = [BitSet<usize>; 4];

fn main() {
    println!("f");

    // Initialize `a` with default BitSets
    let mut a: A = [BitSet::new(), BitSet::new(), BitSet::new(), BitSet::new()];

    // Now you can safely insert into the BitSet
    a[0].insert(3);
}

or for larger arrays

use bit_set::BitSet;
use std::convert::TryInto;

fn main() {
    println!("f");

    // Initialize a vector and fill it with new BitSets
    let mut vec = Vec::with_capacity(36);
    for _ in 0..36 {
        vec.push(BitSet::new());
    }

    // Convert the Vec to an array
    let a: [BitSet<usize>; 36] = vec.try_into().expect("Wrong length");

    // Now you can use the array `a`
    // ...
}

Is this the best available solution?

I like to use array::map.

let mut a: A = [(); 4].map(|_| BitSet::new());
1 Like

Also: from_fn in std::array - Rust

5 Likes

Yes, from_fn is better for this.

1 Like

For small arrays, there’s also a Default implementation, so for your case of length 4, A::default() might just work.

3 Likes

Thank you very much for all your suggestions.

For small arrays, there’s also a Default implementation,

Actually, I tried Default::default() for my whole Game struct, which failed. I still wonder why Default is not available for some types, or only for small container sizes. For my Game struct, I have currently

//#[derive(Default)]
pub struct Game {
    TablePut: int,
    TableCol: int,
    ABCall: int,
    ScoreHashSucc: int,
    FloorHashSucc: int,
    HashSucc: int,
    NullMoveSucc1: int,
    NullMoveSucc2: int,
    ReEvalSkip: int,
    MaxDeltaLen: int,
    ENDG: bool,
    CutTime: std::time::Duration,
    StartTime: std::time::Instant,
    tt: Vec<TTE>,
    debugList: Vec<String>,
    history: HashMap<BitBuffer192, i32>,
    board: Board,
    hasMoved: HasMoved,
    pawnMarch: PawnMarch,
    freedom: Freedom,
    pawnPath: [Path; 2],
    knightPath: Path,
    bishopPath: Path,
    rookPath: Path,
    kingPath: Path,
    pjm: int, // = -1
}

pub fn newGame() -> Game {
  Game{
    TablePut: 0,
    TableCol: 0,
    ABCall: 0,
    ScoreHashSucc: 0,
    FloorHashSucc: 0,
    HashSucc: 0,
    NullMoveSucc1: 0,
    NullMoveSucc2: 0,
    ReEvalSkip: 0,
    MaxDeltaLen: 0,
    ENDG: false,
    CutTime: Duration::new(0, 0),
    StartTime: Instant::now(),
    tt: Vec::new(),
    debugList: Vec::new(),
    history: HashMap::new(),
    board: [0; 64],
    hasMoved: BitSet::new(),
    pawnMarch: [BitSet::new(); 4 + 32 + 1],
    freedom: [[0; 64]; 13],
    //pawnPath: [Path; 2],
    knightPath: [[Gnu{pos: 0, nxtDirIdx: 0}; 64]; 64],
    //bishopPath: Path,
    //rookPath: Path,
    //kingPath: Path,
    pjm: -1,
  }
}

(With "type int = i64;" as a legacy from the original Nim version.)

which is still work in progress. I understand that initialization is important, but all the "= 0" or "= Vec::new()" assignments are not that much fun to type in early prototype state.

I think in a few days I will get the engine and the tiny GTK4 GUI to compile, then I will clean up the code and reformat with rustfmt, and push it to GitHub for further improvements.

Because legacy. Before const generics support it was not possible to abstract over arrays of all possible sizes, so many traits were only implemented for arrays of 32 elements or fewer. Most of those traits now have generic impls for arrays, but due to an unfortunate backwards compatibility issue, Default does not :frowning:

(Instant doesn’t impl Default either, probably because there’s no good default; although it’s not strictly required, the default value should preferably be constant, so now() wouldn’t be a very good default .)

1 Like

It is not that easy for a Rust beginner. Following code fails in the Rust playground, and I have not yet been able to fix it. GTP-4 fails as well.

use bit_set::BitSet;
use core::array;

type A = [BitSet<usize>; 36];

struct S {
    i: i32,
    a: A,
}

fn main() {
    println!("f");

    let a: A = array::from_fn::<_, 36>(|_| BitSet::new());

    //let s: S{i: 0, a: array::from_fn(|_| BitSet::new())}
    let s: S = S{i: 0, a: array::from_fn(|_| BitSet::new())};
}

I think I still do not really understand the generics syntax:

use bit_set::BitSet;
use core::array;

type A = [BitSet<usize>; 36];

struct S {
    i: i32,
    a: A,
}

fn main() {
    println!("f");

    //let a: A = array::from_fn::<_, 36>(|_| BitSet::new());

    
    let s: S = S{i: 0, a: [(); 36].map(|_| BitSet::new())};
}
17 |     let s: S = S{i: 0, a: [(); 36].map(|_| BitSet::new())};
   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `[BitSet<usize>; 36]`, found `[BitSet; 36]`
   |
   = note: expected array `[BitSet<usize>; 36]`
              found array `[BitSet; 36]`

I think I finally found my error:

type A = [BitSet<usize>; 36];

The following code works:

use bit_set::BitSet;
use core::array;

type A = [BitSet; 36];

struct S {
    i: i32,
    a: A,
}

fn main() {
    println!("f");

    let s: S = S{i: 0, a: [(); 36].map(|_| BitSet::new())};
    let s2: S = S{i: 0, a: array::from_fn(|_| BitSet::new())};
}

Don't blame yourself for the confusion, it looks like the crate has a generic parameter B in BitSize<B = u32>, as if B could be changed to other integer sizes, but only implements the struct's methods for u32.

3 Likes