I want to write a function returning a reference to a Dict element. Previously, this could be done by dereferencing the result of Dict.get_ptr(key). Now, as my_dict[key] already returns a reference I thought this should be even more straightforward, but I encounter issues with the origins. Can someone point out my error?
This is my attempt (Mojo 25.4.0.dev2025052405):
@fieldwise_init
struct Element(Copyable, Movable):
var value: Int
@fieldwise_init
struct Container:
var data: Dict[Int, Element]
fn get(ref self, key: Int) raises -> ref [__origin_of(self.data.__getitem__(key))] Element:
return self.data.__getitem__(key)
def main():
s = Container({1: Element(1)})
s.get(1) = Element(2)
print(s.data[1].value)
This complains about the result of get not being mutable, and this remains the case even if I define it as get(mut self, key: Int):
/path/to/module.mojo:16:10: error: expression must be mutable in assignment
s.get(1) = Element(2)
~~~~~^~~
Other attempts
I have also tried the following:
Dereferencing a pointer to the dict element (same error):
/path/to/module.mojo:10:56: error: value of type 'Element' doesn't have a memory origin
fn get(mut self, key: Int) raises -> ref [self.data[key]] Element:
~~~~~~~~~^~~~~
/path/to/module.mojo:10:56: error: cannot infer origin for a function result
fn get(mut self, key: Int) raises -> ref [self.data[key]] Element:
~~~~~~~~~^~~~~
/path/to/module.mojo:16:10: error: expression must be mutable in assignment
s.get(1) = Element(2)
~~~~~^~~
/path/to/module.mojo:11:25: error: cannot bind a non-memory value to a 'ref' argument in return value
return self.data[key]
It seems that Dict.__getitem__() always returns an immutable ref.
Doing the same with a List works fine because it returns a mutable ref:
@fieldwise_init
struct Element(Copyable, Movable):
var value: Int
@fieldwise_init
struct Container:
var data: List[Element]
fn get(ref self, key: Int) raises -> ref [self.data.__getitem__(key)] Element:
return self.data.__getitem__(key)
def main():
s = Container([Element(1)])
print(s.data[0].value)
s.get(0) = Element(2)
print(s.data[0].value)
You might need to use a Pointer for having a mutable ref from a Dict:
from memory import ArcPointer
@fieldwise_init
struct Element(Copyable, Movable):
var value: Int
@fieldwise_init
struct Container:
var data: Dict[Int, ArcPointer[Element]]
fn get(ref self, key: Int) raises -> ref [self.data.__getitem__(key)] ArcPointer[Element]:
return self.data.__getitem__(key)
def main():
s = Container({1: ArcPointer(Element(1))})
print(s.data[1][].value)
s.get(1)[] = Element(2)
print(s.data[1][].value)
BTW: If you would like the Container to behave like a collection type you can replace get() with __getitem__(). You can use a Pointer instead of an ArcPointer too:
from memory import Pointer
@fieldwise_init
struct Element(Copyable, Movable):
var value: Int
alias ElementPointer = Pointer[Element, MutableAnyOrigin]
@fieldwise_init
struct Container:
var data: Dict[Int, ElementPointer]
fn __getitem__(ref self, key: Int) raises -> ref [self.data.__getitem__(key)] ElementPointer:
return self.data.__getitem__(key)
def main():
a = Element(1)
s = Container({1: ElementPointer(to=a)})
print(s.data[1][].value)
s[1][] = Element(2)
print(s.data[1][].value)
Thank you. Yes, I see now that the self in Dict.__getitem__ does not have the ref keyword in front of itself and is thus immutable. I am just not sure whether this is intended behaviour.
The solution with ArcPointer puts all dict elements at random places in memory, so this is not a satisfactory solution though it surely works.
If there is anyone here who can say whether the missing ref keyword is intentional, I would be grateful for a hint as to why it is not there.
You are right. I made a copy of dict.mojo and added the ref self to __getitem__() and it just works fine. A mutable ref is returned and the value can be mutated.