Skip to content

No recommended way to call Base<T> methods from init #585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
MatrixDev opened this issue Jan 30, 2024 · 2 comments
Closed

No recommended way to call Base<T> methods from init #585

MatrixDev opened this issue Jan 30, 2024 · 2 comments
Labels
status: duplicate This issue or pull request already exists

Comments

@MatrixDev
Copy link

MatrixDev commented Jan 30, 2024

Basically what I want to achieve is to have fully constructed tree after init finishes.

I'd also would like to avoid ready because:

  • no way to share data with init outside of Self fields
  • AFAIK ready can be called multiple times if object is readded to the tree so more logic needs to be added to handle this

Gd::from_init_fn also doesn't help because nodes must have init method to be usabled in the editor.

Problem

This code crashes:

fn init(base: Base<Self::Base>) -> Self {
    let mut node = Node3D::new_alloc();
    // do something with node

    let mut this = Self {
        node: node.clone(),
        base,
    };
    this.base_mut().add_child(node.upcast()); // crashes
    this
}

Workaround

I can use a different method to achieve it which doesn't crash:

fn init(base: Base<Self::Base>) -> Self {
    let mut node = Node3D::new_alloc();
    // do something with node

    base.to_gd().add_child(node.clone().upcast()); // works
    Self {
        node,
        base,
    }
}

But... Base::to_gd is #[doc(hidden)] and also is not recommended to use:

Using this method to call methods on the base field of a Rust object is discouraged, instead use the methods from WithBaseField when possible.

Also from The Book

Anything that is not intended to be accessible by the user, but must be pub for technical reasons, should be marked as #[doc(hidden)].

Question

Is there any suggested way to achieve mentioned functionality? It might be that Base has some magical functions but RustRover just doesn't display it to me:
image

Solution 1 (more lazy, less ideal)

Just make Base::to_gd / Base::as_gd a public API.

In that case it would also make sense to add Deref/DerefMut and Base::as_gd_mut.

This is probably the least preferred way becausee we are working with half-constructed Base node instead of fully constructed Self node.

Solution 2

Adding additional overload for init in theory is the best alternative:

fn init_gd() -> Gd<Self> {
    let mut node = Node3D::new_alloc();
    // do something with node

    let mut this = Gd::from_init_fn(|base|
        Self {
            node: node.clone(),
            base,
        }
    );
    this.base_mut().add_child(node.upcast());
    this
}

Solution 3

I don't know if it will work but still an alternative:

fn init_gd(base: Base<Self::Base>) -> Gd<Self> {
    let mut node = Node3D::new_alloc();
    // do something with node

    let mut this = Gd::from_object(Self {
        node: node.clone(),
        base,
    });
    this.base_mut().add_child(node.upcast());
    this
}
@lilizoey lilizoey added the status: duplicate This issue or pull request already exists label Jan 30, 2024
@lilizoey
Copy link
Member

i believe this is a duplicate of #557

@lilizoey lilizoey closed this as not planned Won't fix, can't repro, duplicate, stale Jan 30, 2024
@MatrixDev
Copy link
Author

i believe this is a duplicate of #557

damn... I was searching for a solution for few hours and didn't find that issue...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: duplicate This issue or pull request already exists
Projects
None yet
Development

No branches or pull requests

2 participants