I’m trying to implement simple Memoization while learning Zig. Learning from my questions here about HashMap.getOrPut
, I’ve constructed the following attempted solution to a toy problem:
const std = @import("std");
const print = std.debug.print;
const expect = @import("std").testing.expect;
const FibonacciMemoizer = struct {
data: std.AutoHashMap(u8, u32),
fn findNthFibonacci(self: FibonacciMemoizer, n: u8) u32 {
if (n == 0) {
return 0;
}
if (n == 1) {
return 1;
} else {
const response = try self.data.getOrPut(n);
if (response.found_existing) {
return response.value_ptr.*;
} else {
const return_value = self.findNthFibonacci(n - 1) + self.findNthFibonacci(n - 2);
response.value_ptr = return_value;
return return_value;
}
}
}
};
test "find the 25th Fibonacci number" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var inner_data = std.AutoHashMap(u8, u32).init(allocator);
defer inner_data.deinit();
const memoizer = FibonacciMemoizer{ .data = inner_data };
try expect(memoizer.findNthFibonacci(0) == 0);
try expect(memoizer.findNthFibonacci(1) == 1);
try expect(memoizer.findNthFibonacci(10) == 55);
try expect(memoizer.findNthFibonacci(19) == 4181);
}
However, this gives:
scratch.zig:15:43: error: expected type '*hash_map.HashMap(u8,u32,hash_map.AutoContext(u8),80)', found '*const hash_map.HashMap(u8,u32,hash_map.AutoContext(u8),80)'
const response = try self.data.getOrPut(n);
~~~~~~~~~^~~~~~~~~
scratch.zig:15:43: note: cast discards const qualifier
/Users/scubbo/zig/zig-macos-x86_64-0.14.0-dev.2362+a47aa9dd9/lib/std/hash_map.zig:490:31: note: parameter type declared here
pub fn getOrPut(self: *Self, key: K) Allocator.Error!GetOrPutResult {
I interpret this as saying that the reference to self.data
within the struct’s function is giving an immutable pointer to data
, and thus that self.getOrPut
fails. Fair enough - that seems like correct behaviour. But, then - how do I get a mutable pointer to self.data
?
What I’ve tried
Declaring as variable
I’ve tried declaring the field with var
- i.e.
const FibonacciMemoizer = struct {
var data: std.AutoHashMap(u8, u32),
which appears to be a syntax error.
Changing the function to accept a pointer to Self
I don’t know why this would work - my issue is with the mutability of the internal pointer, not with the reference to self
. However, I’ve seen some suggestions (e.g. here) that the self-reference as the first parameter of a struct’s function should be a pointer, so I tried that…
...
const FibonacciMemoizer = struct {
data: std.AutoHashMap(u8, u32),
fn findNthFibonacci(self: *FibonacciMemoizer, n: u8) u32 { // <--- change here
if (n == 0) {
...
and got
scratch.zig:37:24: error: expected type '*scratch.FibonacciMemoizer', found '*const scratch.FibonacciMemoizer'
try expect(memoizer.findNthFibonacci(0) == 0);
~~~~~~~~^~~~~~~~~~~~~~~~~
scratch.zig:37:24: note: cast discards const qualifier
scratch.zig:8:31: note: parameter type declared here
fn findNthFibonacci(self: *FibonacciMemoizer, n: u8) u32 {
^~~~~~~~~~~~~~~~~~
Similar output arises if I change the test code to create memoizer
as a pointer - that is:
...
var inner_data = std.AutoHashMap(u8, u32).init(allocator);
defer inner_data.deinit();
const memoizer = &FibonacciMemoizer{ .data = inner_data }; // <--- change here
try expect(memoizer.findNthFibonacci(0) == 0);
...
Similar Questions
- This question is similar, but is not the same - it’s about mutable pointers to structs, not getting mutable pointers within a struct’s functions to the struct’s fields
- This question appears to be about internals of compiler operation, and not relevant at this level
- This question makes it clear that “_ arguments passed by value are immutable. The fact that you pass it by value, e.g.
self: Bar
, means that you don’t intend to mutate it_”, which still doesn’t explain the situation:- Even when I was passing
self
by-value rather than as a pointer, I wasn’t trying to mutate the object itself - I was trying to mutate one of its fields (or - have I misunderstood that? Does immutability “cascade” to the fields of an object?) - This suggests that my solution passing
self
as a pointer should have worked - but it didn’t.
- Even when I was passing