Automatically implement Python getters and setters for public Rust struct fields
This would make Rust<->Python intercommunication even more seamless. In Rust, you can access struct fields if they are public - why not in Python?
I can't see any problems if PyO3 automatically made Rust struct fields accessible when appropriate, but I'm happy to learn if there are issues I hadn't considered.
Currently, the best design I can think of is to check:
- Do any of the struct's pub fields have a
#[pyo3(get)]or#[pyo3(set)]attribute?- If yes, apply those, just like in earlier PyO3 versions
- If all fields are marked both
#[pyo3(get)]and#[pyo3(set)](making the behavior identical to the fallback implementation), emit a compiler warning that you can remove all those annotations
- If all fields are marked both
- If no, automatically implement getters and setters for all the pub fields
- If yes, apply those, just like in earlier PyO3 versions
This approach has the benefit that existing structs don't mysteriously change behavior, and new plain-old-data structs can be created painlessly without having to annotate every field.
Its disadvantage is that this behavior may be slightly confusing to end users: all fields are accessible by default, but if you add a #[pyo3(get)], suddenly all other fields are not accessible from Python anymore?
Does someone have an idea to improve upon this design, or is there a better design idea altogether?
Why does it have to be this implicit? A single new attribute or argument (on the struct, not the members) isn't too much to write, and makes it explicit that accessors are automatically generated.
I.e.
#[pyclass(pod)]
struct Pod {
x: i32,
y: i32
}
or
#[pyclass]
#[pyo3(get_set_all)]
Bikesheds welcome...
👍 I am strongly in favour of making this an explicit opt-in; doing things automatically is not very idiomatic in Rust. I think it can be the same attribute as for #1376.
#[pyclass(pod)] or #[pyclass(dataclass)] is quite reasonable as a suggestion. It should require all fields are public and could automatically provide #[new] as well as getters and setters for all the fields.
In that case, I think #[pyclass(dataclass)] is preferable over #[pyclass(pod)] or #[pyclass(get_set_all)]. "pod" is potentially confusing for people who don't know the term. "get_set_all" is explicit, but it can't be used as the same attribute for #1376. "dataclass" references an existing well known Python concept and concisely encapsulated the behavior both from this feature and from #1376.
We may want to also consider the existence of such an attribute when thinking about design for #[pyenum] we have proposed for #1045, and #[pyexception] in #295 . Should they become #[pyclass(enum)] and #[pyclass(exception)] for consistency?
In that case, I think #[pyclass(dataclass)] is preferable over #[pyclass(pod)]
Note that @dataclass in Python also adds hash, repr and ordering - so we may want to provide an implementation of PyObjectProtocol also, to match Python as best as possible.
Are there any new on this issue? Any plans to implement #[pyclass(dataclass)] in the coming future?
Are there any new on this issue? Any plans to implement
#[pyclass(dataclass)]in the coming future?
We did add [pyclass(get_all, set_all)] in 0.18, so if all you want is to generate getters and setters for all fields you can use that. Also, frozen exists, though I am not sure it is an exact match to a dataclass' frozen.
As for other features associated with "data classes", like constructors, dunder method implementations etc I don't think anyone is working on that. It would be up to someone to come up with a design and an implementation.
We did add
[pyclass(get_all, set_all)]in 0.18, so if all you want is to generate getters and setters for all fields you can use that
I think one of the main asks for a #[pyclass(dataclass)] was to remove the need for an explicit #[new], which still won't be achieved with this solution.