Skip to content

Safety redesign #808

Open
Open
@Bromeon

Description

@Bromeon

godot-rust historically (v0.8) used unsafe for every interaction with the engine. While this was "on the safe side", it was very inconvenient and forced the user to clutter the code with unsafe. v0.9 took a different approach with type-states: the user opts in to unsafety when converting Ref to TRef, and most (but not all) subsequent calls are safe. This improved a lot, but unfortunately there's still quite a bit of friction (#758 among others).

The more we looked into safety models, the more it became apparent that there's quite a bit of work left to do to come up with something consistent and uniformly applicable. Most notable, the following questions remain unanswered:

  • What assertion does the user precisely make with Ref::assume_safe()? What about the safe ways to obtain TRef (owner parameter)? Object lifeness is not enough.
  • Since calls to GDScript can execute arbitrary code, should every such invocation be marked unsafe? Or should we draw the boundary at Rust code, leaving the responsibility for correct GDScript implementation to the user? For the record, these are possible causes for UB when calling GDScript:
    • Destruction of objects in use, e.g. through Object::free() or Reference::unreference().
    • Race conditions -- GDScript has full access to its own Threadclass
    • Calls back to Rust via GDNative, i.e. execution of methods marked unsafe from safe code
    • Bugs in the C++ code of the engine (not worth protecting against)
  • How can we ensure that unsafe doesn't turn into an inflationary keyword that's used mindlessly whenever any Godot APIs are used? There is no point in being on the "academically correct side" if people start using unsafe like it means nothing. Ideally, unsafe has the following purposes:
    • to limit the UB to few parts of Rust code, which can be the focus of auditing
    • to make developers re-think if they really need unsafe whenever they write it
    • to make developers alert when they see it in existing code, and treat the surrounding logic with caution

A new model would ideally be consistent in the way that it's clear which methods are unsafe and which aren't, i.e. there should be a ruleset for inclusion/exclusion (with only few exceptions). It should also be pragmatic for above-mentioned reasons, a dogmatic "everything is unsafe" is not helping anyone. We have to embrace the fact that godot-rust's entire point is to deal with non-Rust code, whose safety is outside the library's control.


This issue hosts general discussion and this post serves as tracker for implementation tasks.

Large issues needing considerable design:

More isolated, specific API shortcomings:

Metadata

Metadata

Assignees

No one assigned

    Labels

    breaking-changeIssues and PRs that are breaking to fix/merge.quality-of-lifeNo new functionality, but improves ergonomics/internalstrackerGroups a list of related issues and tracks progress

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions