Skip to content

Make #[var(get, set)] orthogonal#1454

Merged
Bromeon merged 2 commits intomasterfrom
qol/orthogonal-get-set
Dec 28, 2025
Merged

Make #[var(get, set)] orthogonal#1454
Bromeon merged 2 commits intomasterfrom
qol/orthogonal-get-set

Conversation

@Bromeon
Copy link
Copy Markdown
Member

@Bromeon Bromeon commented Dec 27, 2025

TLDR: in #[var] attribute, keys get and set are now orthogonal (independent).

Closes #1263.

Breaking changes

1. Orthogonality

Previously, specifying one of get or set automatically opted out the other of the two (meaning no getter/setter would be generated). A user would need to explicitly define #[var(get = my_getter, set)] to add the generated setter back.

2. Custom (user-defined) accessors with default name

The syntax #[var(get)] and #[var(set)] now changes semantics, and intends to become the main way of providing custom getters/setters.

It used to mean "provide a generated getter/setter".
In v0.5, it means "user-generated getter/setter with conventional name".
The convention is a get_ or set_ prefix.

So, if you write:

#[var(get)]
my_property: GString,

this will expect in the #[godot_api] block the following function:

#[func]
fn get_my_property(&self) -> GString { ... }

So, this is equivalent to #[var(get = get_my_property)], but with the advantage or disadvantage that changing field name also changes accessor name. In practice, #[var(get)] however avoids repetition and intends to become the standard way of using custom getters or setters.

3. Disabling getters and setters

Disabling a getter or setter required opting in to the other accessor. Now, there is a dedicated syntax:

  • #[var(no_set)] disables the setter.
    • Used to be #[var(get)]; omitting a setter implicitly disabled it.
  • #[var(no_get)] disables the getter.
    • Used to be #[var(set)].
    • Not having a getter does not work nicely with the inspector UI in the Godot editor.

Overview of changes to existing syntax

Syntax Old behavior Behavior in v0.5
#[var] generated getter + setter generated getter + setter
#[var(get = fn, set = fn)] custom getter, custom setter custom getter, custom setter
#[var(get)] generated getter
no setter (read-only)
⚠️ custom get_field
generated setter
#[var(get = fn)] custom getter
no setter (read-only)
⚠️ custom getter
generated setter
#[var(get, set)] generated getter + setter
(like #[var])
⚠️ custom getter + setter
#[var(get = fn, set)] custom getter, generated setter ⚠️ custom getter fn
custom set_field
#[var(get, set = fn)] generated getter, custom setter ⚠️ custom get_field
custom setter
#[var(no_set)] n/a generated getter
no setter (read-only)
#[var(get, no_set)] n/a custom getter
no setter (read-only)

@Bromeon Bromeon added this to the 0.5 milestone Dec 27, 2025
@Bromeon Bromeon added quality-of-life No new functionality, but improves ergonomics/internals c: register Register classes, functions and other symbols to GDScript breaking-change Requires SemVer bump labels Dec 27, 2025
@Bromeon Bromeon force-pushed the qol/orthogonal-get-set branch from ed3a3fd to 0fc8f58 Compare December 27, 2025 21:44
@GodotRust
Copy link
Copy Markdown

API docs are being generated and will be shortly available at: https://godot-rust.github.io/docs/gdext/pr-1454

@Yarwin
Copy link
Copy Markdown
Contributor

Yarwin commented Dec 27, 2025

So, if you write:

#[var(get)]
my_property: GString,

this will expect in the #[godot_api] block the following function:

#[func]
fn get_my_property(&self) -> GString { ... }

What is the compile error message if said getter(/setter) is not found? (we can always improve etc it later, but it is good to note what we start with)

@Bromeon Bromeon force-pushed the qol/orthogonal-get-set branch from 0fc8f58 to 4a482e8 Compare December 27, 2025 22:54
@Bromeon
Copy link
Copy Markdown
Member Author

Bromeon commented Dec 27, 2025

What is the compile error message if said getter(/setter) is not found? (we can always improve etc it later, but it is good to note what we start with)

image

As always, spans suck -- if you have an idea, would be nice (outside this PR) 🤔

In the meantime I'll consolidate the tests, there is now quite some duplication with old stuff...

@Bromeon Bromeon force-pushed the qol/orthogonal-get-set branch 2 times, most recently from e346e6c to 682b276 Compare December 28, 2025 07:00
…` fields

Also remove `Texture` from minimal-codegen; used only in this place and get/set
is not specific to this class.
@Bromeon Bromeon force-pushed the qol/orthogonal-get-set branch from 682b276 to 8937052 Compare December 28, 2025 09:12
Comment on lines +31 to +32
#[var(get = get_resource_rw, set = set_resource_rw, hint = RESOURCE_TYPE, hint_string = "Resource")]
resource_rw: Option<Gd<Resource>>,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was an interesting bug:

#[var(get = get_texture_val, set = set_texture_val, ...)]

refer to the getters/setters of the previous field. This happens to work due to having the same signature, and we don't have any tests actually accessing the property. I've added GDScript tests for this now.

But also, it's an indicator that the auto-generated getters/setters that are publicly accessible in Rust are not always desirable. We might want to reconsider this... 🤔

@Bromeon Bromeon added this pull request to the merge queue Dec 28, 2025
Merged via the queue into master with commit dfc48c3 Dec 28, 2025
20 checks passed
@Bromeon Bromeon deleted the qol/orthogonal-get-set branch December 28, 2025 09:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change Requires SemVer bump c: register Register classes, functions and other symbols to GDScript quality-of-life No new functionality, but improves ergonomics/internals

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Getters and Setters are both opt-in and opt-out, when they probably should be opt-out

3 participants