A fixed size version of a dynamic struct

Probably the following is possible. Maybe someone has a brilliant way…

I have this generic Map which basically stores an array of width * height elements and lots of methods to operate on that 2-dimensional data using indexing y * width + x.

pub fn Map(comptime T: type) type
{
    return struct
    {
        allocator: std.mem.Allocator,
        width: u32,
        height: u32,
        data: []T,

        // ... lots of methods...
    };
}

Now I would like to have a fixed version as well without duplicating code.

// const len = comptime known width / height
data: [len]T

And what about the allocator?

A fixed version would have the benefit of not having to free resources.

Another problem is that some operations are not possible in the fixed version of the Map, like resize. There we could maybe insert @compileError("not allowed on fixed map");

And: is it a good idea anyway to do this?

I actually think the standard library should do what you’re talking about here in their own containers. There’s currently a lot of duplicated logic between BoundedArray and ArrayList. I started writing a PR for it but never finished.
What you need to do is extract everything that differs between the concrete implementations into an object that you receive as a parameter:

pub fn Map(comptime T: type, comptime Memory: type) type {
    return struct{
        memory: Memory
        width: u32,
        height: u32,

        pub fn get(self: *@This(), x: u32, y: u32) *T{
          const data: []T = self.memory.data();
          return &data[getIndex(x, y)];
        }
    };
}

fn DynamicMemory(comptime T: type) type{
  return struct{
     allocator: Allocator,
     slice: []T,

     pub fn data(self: *@This()) []T{
       return self.slice;
     }
  };
}

fn FixedMemory(comptime T: type, comptime size: usize) type{
  return struct{
     array: [size]T

     pub fn data(self: *@This()) []T{
       return &self.array;
     }
  };
}
1 Like

I was a bit experimenting and turned it like this (code not 100% correct but you get the idea probably). And not yet sure if it would work…
The thing is restricting some methods with compile-errors and no allocator in case of fixed.

pub const Size = struct 
{ 
    width: u32,  height: u32 
};

pub fn Map(comptime T: type) type
{
    return VolatileMap(T, null);
}

pub fn FixedMap(comptime T: type, comptime size: Size) type
{
    return VolatileMap(T, size);
}

pub fn VolatileMap(comptime T: type, comptime size: ?Size) type
{
    return struct
    {
        const IS_FIXED: bool = if (size) |s| true else false;
        const DataType: if (size) |s| [size.width * size.height]T else []T;
        allocator: std.mem.Allocator,
        width: u32,
        height: u32,
        data: DataType,

        // ... lots of methods...
    };
}