M
Modular•10mo ago
EzRyder

Mojo Dict: Store struct instances based on a common trait as CollectionElement.

I'm trying to use a Mojo Dict to store different instances of structs which conform to a common trait. I've created a 'playground' file to work out a solution, but I am stumped. Feels like I'm really close, but no joy yet. Any suggestions? GitHub: https://github.com/johnsoez4/dict The code snippet below uses the structs defined earlier in the file. The last line below causes the error:
'Dict' parameter #1 has 'CollectionElement' type, but value has type 'Pet[?]'mojo
dict_playground.mojo(1, 1): 'Dict' declared here
struct Pet[T: TPet]
'Dict' parameter #1 has 'CollectionElement' type, but value has type 'Pet[?]'mojo
dict_playground.mojo(1, 1): 'Dict' declared here
struct Pet[T: TPet]
fn main() raises:
alias cats = "Cats"
alias dogs = "Dogs"

var my_pet = MyPet(cats)
var your_pet = YourPet(dogs)

var my_cats = Pet(cats, my_pet)
my_cats.start("champagne.")

var my_dogs = Pet(dogs, your_pet)
my_dogs.start("blueberries.")

# var d = Dict[StringKey, CollectionElement]() # Compiler crashes
var d = Dict[StringKey, Pet]() # Error here
fn main() raises:
alias cats = "Cats"
alias dogs = "Dogs"

var my_pet = MyPet(cats)
var your_pet = YourPet(dogs)

var my_cats = Pet(cats, my_pet)
my_cats.start("champagne.")

var my_dogs = Pet(dogs, your_pet)
my_dogs.start("blueberries.")

# var d = Dict[StringKey, CollectionElement]() # Compiler crashes
var d = Dict[StringKey, Pet]() # Error here
GitHub
GitHub - johnsoez4/dict
Contribute to johnsoez4/dict development by creating an account on GitHub.
7 Replies
Michael K
Michael K•10mo ago
Mark Pet as conforming to CollectionElement. Like this:
struct Pet[T: TPet](CollectionElement):
struct Pet[T: TPet](CollectionElement):
If it is a simple struct that is not allocating or owning any pointers then mark it with @value decorator. That will ensure it has the functions necessary to be moved and copied the way Collections expect.
EzRyder
EzRyderOP•10mo ago
Thanks for the suggestion Michael! The code below is the Pet struct from my 'playground' example. As you can see, it already matched your suggestion, but Mojo flags it with the error described in my initial post. I just added the @ value with no discernible effect. Here's a link to my 'playground' file with all the code. GitHub: https://github.com/johnsoez4/dict
@value # <-- Added 2024-03-14 EzRyder
struct Pet[T: TPet](CollectionElement):
var name: String
var pet: T

fn __init__(inout self, name: String, pet: T):
self.name = name
self.pet = pet

fn __copyinit__(inout self, existing: Self):
self.name = existing.name
self.pet = existing.pet

fn __moveinit__(inout self, owned existing: Self):
self.name = existing.name
self.pet = existing.pet

fn __del__(owned self):
pass

fn start(self, edible: String):
self.pet.start(edible)
@value # <-- Added 2024-03-14 EzRyder
struct Pet[T: TPet](CollectionElement):
var name: String
var pet: T

fn __init__(inout self, name: String, pet: T):
self.name = name
self.pet = pet

fn __copyinit__(inout self, existing: Self):
self.name = existing.name
self.pet = existing.pet

fn __moveinit__(inout self, owned existing: Self):
self.name = existing.name
self.pet = existing.pet

fn __del__(owned self):
pass

fn start(self, edible: String):
self.pet.start(edible)
GitHub
GitHub - johnsoez4/dict
Contribute to johnsoez4/dict development by creating an account on GitHub.
EzRyder
EzRyderOP•10mo ago
I just made one more change in the main function to use 'CollectionElement' instead of 'Pet' to define the dictionary (see below). The VS Code IDE is happy with this change, but the compiler throws this error:
<unknown>:0: error: failed to legalize operation 'kgen.param.constant' that was explicitly marked illegal
<unknown>:0: note: see current operation: %5 = "kgen.param.constant"() {value = #kgen.unknown : !kgen.variant<struct<() memoryOnly>, struct<(index, struct<(struct<(struct<(pointer<none>, index, index) memoryOnly>) memoryOnly>) memoryOnly>, type) memoryOnly>>} : () -> !kgen.variant<struct<() memoryOnly>, struct<(index, struct<(struct<(struct<(pointer<none>, index, index) memoryOnly>) memoryOnly>) memoryOnly>, type) memoryOnly>>
/home/johnsoe1/.modular/pkg/packages.modular.com_max/bin/mojo: error: Failed to materialize symbols: { (exec, { KGEN_EE_JIT_GlobalDestructor, main, KGEN_EE_JIT_GlobalConstructor }) } (from the layer: failed to lower module to LLVM IR for archive compilation)
<unknown>:0: error: failed to legalize operation 'kgen.param.constant' that was explicitly marked illegal
<unknown>:0: note: see current operation: %5 = "kgen.param.constant"() {value = #kgen.unknown : !kgen.variant<struct<() memoryOnly>, struct<(index, struct<(struct<(struct<(pointer<none>, index, index) memoryOnly>) memoryOnly>) memoryOnly>, type) memoryOnly>>} : () -> !kgen.variant<struct<() memoryOnly>, struct<(index, struct<(struct<(struct<(pointer<none>, index, index) memoryOnly>) memoryOnly>) memoryOnly>, type) memoryOnly>>
/home/johnsoe1/.modular/pkg/packages.modular.com_max/bin/mojo: error: Failed to materialize symbols: { (exec, { KGEN_EE_JIT_GlobalDestructor, main, KGEN_EE_JIT_GlobalConstructor }) } (from the layer: failed to lower module to LLVM IR for archive compilation)
Alternate var d = DictStringKey, CollectionElement # Compiler crashes
fn main() raises:
alias cats = "Cats"
alias dogs = "Dogs"

var my_pet = MyPet(cats)
var your_pet = YourPet(dogs)

var my_cats = Pet(cats, my_pet)
my_cats.start("champagne.")

var my_dogs = Pet(dogs, your_pet)
my_dogs.start("blueberries.")

var d = Dict[StringKey, CollectionElement]() # Compiler crashes
# var d = Dict[StringKey, Pet]() # Error here
fn main() raises:
alias cats = "Cats"
alias dogs = "Dogs"

var my_pet = MyPet(cats)
var your_pet = YourPet(dogs)

var my_cats = Pet(cats, my_pet)
my_cats.start("champagne.")

var my_dogs = Pet(dogs, your_pet)
my_dogs.start("blueberries.")

var d = Dict[StringKey, CollectionElement]() # Compiler crashes
# var d = Dict[StringKey, Pet]() # Error here
Michael K
Michael K•10mo ago
You need to provide the complete type as a parameter to Dict. Pet requires a parameter T and without that being specificed it is not concrete type that can be passed to Dict. so like this:
var d = Dict[StringKey, Pet[MyPet]]()
var d = Dict[StringKey, Pet[MyPet]]()
Also I think Dict now works witht a plain String. No need to use StringKey.
EzRyder
EzRyderOP•10mo ago
var my_pet = MyPet(cats)
var your_pet = YourPet(dogs)
var my_pet = MyPet(cats)
var your_pet = YourPet(dogs)
This only works for MyPet (i.e. cats), but not for YourPet (i.e. dogs). Both MyPet and YourPet structs use the trait TPet defined previously in the file. So I'm trying to assign the Dict with TPet instead of MyPet: var d = Dict[StringKey, Pet[TPet]]() # Error here But get this error:
'Pet' parameter #0 has 'TPet' type, but value has type 'AnyRegType'mojo
dict_playground.mojo(92, 1): 'Pet' declared here
(trait) trait TPet
'Pet' parameter #0 has 'TPet' type, but value has type 'AnyRegType'mojo
dict_playground.mojo(92, 1): 'Pet' declared here
(trait) trait TPet
I just removed StringKey and synced the latest to GitHub. Only works for MyPet, not YourPet.
Michael K
Michael K•10mo ago
I don't think you can use a trait as the parameter when you need to make a concrete type. You need to pass an actual fully known type to Dict. using Pet[YourPet] works for me. If you want a Dict that holds a Pet with T either MyPet or YourPet, I think you would need to use a Variant[MyPet, YourPet]. I am not sure how well that will work but that is how you allow for two possible concrete types.
EzRyder
EzRyderOP•10mo ago
I was hoping for a different answer...this seemed like an elegant solution. 😀 I'd like to store any type that adheres to the trait, even ones that haven't been written yet. I'll play with the Variant and see how it goes. Thanks Michael!
Want results from more Discord servers?
Add your server