#How can I avoid loosing type info here?

144 messages · Page 1 of 1 (latest)

wise karma
#
pub struct Pool {
    nodes: HashMap<TypeId, Box<dyn Any>>, // HashMap<TypeId, Box<SlotMap<DefaultKey, ?>>>
    processes: HashMap<TypeId, Box<dyn Any>>, // HashMap<TypeId, Box<?>>
}
    /// Run all node processes.
    fn process(&self) {
        for process_type in self.processes.keys() {
            let process = self.processes.get(process_type).unwrap();
            process(Vec::new());
        }
    }

    /// Add a process for the specified generic node type.
    fn add_process<T: 'static>(&mut self, process: &'static fn(nodes: Vec<RefMut<'_, Node<T>>>)) {
        let _: &fn(nodes: Vec<RefMut<'_, Node<T>>>) = process;
        self.processes.insert(TypeId::of::<T>(), Box::new(process));
    }

once the 'process'es are stored, they become Any so I can't call it anymore, any idea how I could avoid loosing the type?

peak pond
#

what lol

#

isn't it defeating the purpose of a typemap

#

by having two?

#

anyways, i dont think functions are downcastable. maybe use a more proper trait than Any

limber crane
#
for process_type in self.processes.keys() {
    let process = self.processes.get(process_type).unwrap();

looks like

for (process_type, process) in self.processes.iter() {
#

and process_type isnt even used so for process in self.processes.values() {

#

and use Box<dyn Fn(...) -> ...>

#

or store the fn pointers

peak pond
#

also it's kinda funny that you're now implementing the S part of ECS

abstract sleet
#

Type maps aren't very iterable, as you've just found out.

You could replace Any with a dyn Fn(SomeFormOfAccessToYourState) and let each function be responsible for fetching whatever it needs, though

#

That's more or less what Axum and Bevy do. Bevy does it very complicatedly, partly because it wants to support automatically multithreading these functions, but the basic idea is the same.

peak pond
#

i love the carcinization of ECS

wise karma
wise karma
wise karma
wise karma
wise karma
#

contributes to the elegant api

wise karma
wise karma
wise karma
#

any idea why it expects a lifetime annotation? I'm confused

    |
134 |     pub fn add_process<T: 'static>(&mut self, process: &fn(nodes: Vec<RefMut<Node<T>>>)) {
    |                                                        - let's call the lifetime of this reference `'1`
...
147 |         self.processes.push(Box::new(process));
    |                             ^^^^^^^^^^^^^^^^^ coercion requires that `'1` must outlive `'static`
peak pond
#

ur storing a reference?

#

why have a reference to a function pointer anyway

wise karma
#

I'm taking self as an arg to the closure

peak pond
#

process: &fn(nodes: Vec<RefMut<Node<T>>>)

#

why not process: fn(nodes: Vec<RefMut<Node<T>>>)

wise karma
peak pond
#

who said that lol

wise karma
#

cause they have really arbitrary sizes so I thought it'd be bad for alignment

peak pond
#

i mean, ur accepting a fn pointer

#

which is always the size of a pointer

#

and your boxing it anyways, so the box is always the size of a fat pointer

wise karma
peak pond
#

huh

#

&fn is a pointer to a pointer

limber crane
#

fn is Copy anyway so a reference doesnt make much sense

wise karma
#

fn is always a pointer to begin with?

peak pond
#

fn is called a function pointer, so yea

#

as opposed to function items and boxed Fn etc

wise karma
# peak pond `fn` is called a function pointer, so yea

now it says:

134 |     pub fn add_process<T: 'static>(&mut self, process: fn(nodes: Vec<RefMut<Node<T>>>)) {
    |                                               ------- binding `process` declared here
135 |         // Define a wrapper closure so the generic type can be stored.
136 |         let process = |forest: &Forest| {
    |                       ----------------- value captured here
...
145 |             process(nodes);
    |             ^^^^^^^ borrowed value does not live long enough
146 |         };
147 |         self.processes.push(Box::new(process));
peak pond
#

why does that closure capture by borrow lol

#

can you do move |forest|{...}

wise karma
peak pond
#

read the book :p

#

at least on the chapter on closures

wise karma
#

but I've never used closures in any language before so like... I don't even know what I'm dealing with. I just heard closures can capture their environment... whatever that means ferrisClueless

wise karma
#

oh wait no, I just caused another issue

wise karma
wise karma
limber crane
#

clippy should tell you that, run it

#

can the type of Forest::nodes be HashMap<TypeId, SlotMap<DefaultKey, Box<dyn Any>>>

#

and document that get_node may panic when things are borrowed twice

#

document panics and errors in general

wise karma
#

why the " ` " ?

wise karma
#

oh btw why is this a very complex type while the forest.nodes isn't?

wise karma
wise karma
limber crane
wise karma
# limber crane what

Whether failure to get a reference is recoverable would depend on the usecase, so I should return an error rather than panic here is what I'm saying

limber crane
#

yes

#

but document the borrow_mut panic

wise karma
limber crane
#

thats what you want to do

limber crane
#

then use the ref returned by it

#

to push a node

#

without relooking it up

wise karma
limber crane
#

but downcasting for no reason

wise karma
#

oh yeah, the downcasting restores the type info right?

limber crane
#

yeah

wise karma
#

great ferrisParty

limber crane
wise karma
wise karma
limber crane
limber crane
#

because there are many types in a slotmap

#

wait are all the node Ts the same in a forest

#

probably not

wise karma
wise karma
#

oh wait do you mean are the Ts in the forest definition the same as the Ts in Node's definition, or do you mean are all types stored in forest the same generic type?

limber crane
#

are they all the same

wise karma
#

it could even be an i32

limber crane
#

why not HashMap<TypeId, Box<dyn Any>>

#

and no slotmap in the box

wise karma
limber crane
#

sets of types?

#

not sure what the processes do

wise karma
#

where my_process() gets Vec<T> passed to it

limber crane
#

why not pass it an iterator

wise karma
limber crane
#

is this a weird way to do ecs

limber crane
limber crane
wise karma
limber crane
#

yeah

wise karma
limber crane
#

but do you really need to allocate another vec

wise karma
limber crane
#

why not pass it the slotmap

#

or pass it slotmap.values()

#

it avoids allocating

wise karma
wise karma
limber crane
#

how

#

just immutable access to it

wise karma
limber crane
#

then slotmap.values()

wise karma
limber crane
#

if you expect only one then you might want Itertools::exactly_one to assert that you get one from an iter

#

type_pool.values()

wise karma
#

would a for loop on a one value iter be optimized out anyways?

abstract sleet
#

If your iterator comes from a slotmap that is manipulated in a lot of your code, it probably can't

wise karma
#

oh :(

#

is a for loop on 1 element really slow anyways?

limber crane
#

no

wise karma
#

oh ok then I might just leave it at that cause I doubt creating the Vec is faster anyways...

wise karma
limber crane
#

map borrow_mut

wise karma
#

I've fixed up the doc comments, and swapped the vec for an iterator. Anything you think could still be improved?

#

I'm still panicking if there's multiple RefMut at the moment, but I did at least document that.

#

I'll probably switch to erroring instead soon though

wise karma
#

oh and should I stick with the name Node?

wise karma
#

btw the lack of children is intentional; contrary to parents, there is no one right way to store children so I leave it up to the individual node types.