jbaublitz / getset Goto Github PK
View Code? Open in Web Editor NEWGetters and Setters for Rust.
Home Page: https://docs.rs/getset/
License: MIT License
Getters and Setters for Rust.
Home Page: https://docs.rs/getset/
License: MIT License
Most, if not all getters (generic function parameters are currently unstable) can be const functions
https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
It would be very useful if the getters and Setters could implement a trait fir the structure, instead of adding the implementation directly on the struct.
i.e for:
#[derive(Getters)]
#[getset(trait=GetXY)]
struct Foo {
x: f32
y: f32
}
#[derive(Getters)]
#[getset(trait=GetXY)]
struct Bar {
x: f32
y: f32
}
this would generate:
impl GetXY for Foo {
fn x(&self) -> &f32 {
&self.x
}
fn y(&self) -> &f32 {
&self.y
}
}
impl GetXY for Bar {
fn x(&self) -> &f32 {
&self.x
}
fn y(&self) -> &f32 {
&self.y
}
}
Instead of:
impl Foo {
fn x(&self) -> &f32 {
&self.x
}
fn y(&self) -> &f32 {
&self.y
}
}
impl Bar {
fn x(&self) -> &f32 {
&self.x
}
fn y(&self) -> &f32 {
&self.y
}
}
The attributes could also be field level. E.g
#[derive(Getters)]
struct Bar {
#[getset(trait=GetX)]
x: f32
y: f32
}
This would allow users to create some very powerful abstractions.
For example
fn move(pos: impl GetMutXY, vel: impl GetVelCopy, dt: f32) {
*pos.x() += vel.vx() * dt;
*pos.y() += vel.vy() * dt;
}
This function will work with anything that has getmut getters for x and y, and getcopy getters for vx and vy.
You could create a dynamics library with functions that do not force the user to use a certain datastructure. They could derive the implementations for their own types and then use these functions freely.
This is a more generic solution compared to manually writing custom derives for the traits such as GetMutXY. If you have lots of traits like this, it would be infeasible to write a derive macro for all of them.
I've been using getset on some configuration structs. I use it to derive almost all the getters, but I have some that are boolean and in the struct it makes sense to have them as enable_some_thing
or allow_other_thing
but when I use a function to query them it is more readable as some_thing_enabled
or other_thing_allowed
. I could just do the renaming through serde as a work around or just write the functions out like I've been doing instead of using getset but it would be nice to have the ability like serde to rename what is generated. It will probably require the current proc macro api annotations to change, but I think it would be quite useful.
I would highly recommend you to test not only that your crate works where intended but also that it fails where intended. I recommend trybuild
for that.
Hey @Hoverbear, thanks for this handy crate ๐ธ
I noticed that it's not possible to overwrite get
with get_copy
, e.g.
use getset::{CopyGetters, Getters};
#[derive(Getters, CopyGetters)]
#[get = "pub"]
struct Foo {
a: String,
b: String,
c: String,
#[get_copy = "pub"]
d: u64,
}
doesn't compile with
error[E0592]: duplicate definitions with name `d`
--> src/main.rs:3:19
|
3 | #[derive(Getters, CopyGetters)]
| ------- ^^^^^^^^^^^ duplicate definitions for `d`
| |
| other definition for `d`
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
For more information about this error, try `rustc --explain E0592`.
error: could not compile `foo`
The workaround is to use field level attributes on every property:
use getset::{CopyGetters, Getters};
#[derive(Getters, CopyGetters)]
struct Foo {
#[get = "pub"]
a: String,
#[get = "pub"]
b: String,
#[get = "pub"]
c: String,
#[get_copy = "pub"]
d: u64,
}
Sorry for my long absence, I was very busy..
I'm glad that we now have the way to specify getters that should return by value, but would it be possible to allow the #[get_copy]
attribute when only deriving Getters
? Not requiring an extra derive (CopyGetters
)?
Btw, what was the reason that an extra derive was added for this? I don't think there is any potential for mistakes (like accidentally specifying the wrong kind of getter), and in fact, would it be possible to allow all of getset's attributes just by deriving GetSet
?
Sometimes I start out with only deriving Getters
and then I add some copy-getter or setter attributes but forget to also derive CopyGetters
/Setters
and it's one more cargo cycle to notice that..
It would be much more convenient to only have to derive(GetSet)
, because usually I need getters and setters anyway, and since the goal of this crate is to add convenience & save typing, I think it'd make it even more convenient to enable all attributes at once, what do you think? :)
In the following scenario:
use getset::{Getters, Setters};
#[derive(Default, Getters, Setters)]
#[with_prefix]
#[getset(get="pub with_prefix", set="pub")]
pub struct MyStruct{
x: f64
}
the method get_x
does not get generated.
I also tried this, but it doesn't seem to be handled:
use getset::{Getters, Setters};
#[derive(Default, Getters, Setters)]
#[getset(get="pub with_prefix", set="pub")]
pub struct MyStruct{
x: f64
}
It is only generated if with_prefix
is declared for x like this:
#[derive(Default, Getters, Setters)]
#[getset(set="pub")]use getset::{Getters, Setters};
#[derive(Default, Getters, Setters)]
#[getset(get="pub with_prefix", set="pub")]
pub struct MyStruct{
x: f64
}
pub struct MyStruct{
#[get="pub with_prefix"]
x: f64
}
Dependabot updated proc-macro-error on master. It would be great to have a release with that applied. Technically speaking, with the version number that's the only difference between 0.1.0 and master.
It would be great if there would be an option to use this for Traits. However I think this may require a special syntax(?).
#[derive(MutGetters, Setters)]
pub struct Stdio {
r#in: Option<ChildStdin>,
out: Option<ChildStdout>,
err: Option<ChildStderr>,
}
will cause
error: proc-macro derive panicked
--> src/task.rs:118:10
|
118 | #[derive(MutGetters, Setters)]
| ^^^^^^^^^^
|
= help: message: `"r#in_mut"` is not a valid identifier
error: proc-macro derive panicked
--> src/task.rs:118:22
|
118 | #[derive(MutGetters, Setters)]
| ^^^^^^^
|
= help: message: `"set_r#in"` is not a valid identifier
i know concat_idents
macro handles this properly but it's not stable
I think it'd be better to have the attributes like this
#[derive(Getters, Setters, MutGetters)]
struct Hello {
#[getter(cfgoption1, cfgoption2)]
#[setter(cfgoption1, cfgoption2)]
#[mutgetter(cfgoption1, cfgoption2)]
value: String,
#[setter(skip)]
#[getter(skip)]
#[mutgetter(skip)]
skipme: (),
}
Essentially it'd be nice to have the same syntax, that derive_builder has :)
It might be better to fuse them into a single trait
#[derive(GetSet)]
struct Hello {
#[getset(get, set = "pub(crate)", get_mut = "pub")]
value: String,
#[getset(hidden)]
skipme: ()
}
Currently getset
seems to work with ordinary structs but not with tuple structs:
#[derive(getset::Getters)]
pub struct MyType(u64);
1 error: Expected the field to have a name
--> crates/entities/src/newtypes.rs:265:39
|
265 | pub struct NonNegativeTimeDeltaMicros(u64);
The following also fails:
#[derive(getset::Getters)]
pub struct MyType(#[getset(get)] u64);
It would be convenient to derive get
, set
, etc for unary tuple structs.
This naming convention has precedent in the standard library. For example, we have std::num::NonZeroU64::get
which returns the u64
type that is wrapped by the NonZeroU64
tuple struct.
#[derive(Getters, CopyGetters)]
#[getset(get)]
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct Rank {
#[getset(get_copy)]
val: RankValue,
desc: Desc,
}
emits...
impl Rank {
#[inline(always)]
fn val(&self) -> &RankValue {
&self.val
}
#[inline(always)]
fn desc(&self) -> &Desc {
&self.desc
}
}
impl Rank {
#[inline(always)]
fn val(&self) -> RankValue {
self.val
}
}
The intention is to have all fields get by ref except for val
which should get by Copy
(without having to revert to specifying at the field-level for all fields).
Please, improve the crate documentation.
I suggest adding specific documentation to the derive macros sections, possibly with examples.
In particular the documentation does not say anything about CopyGetters and MutGetters. The signature of the generated methods is not clear for example.
Also the options are not sufficiently described IMO
Often you've structs like
struct MyStruct {
field_a: TypeA,
field_b: TypeB
}
struct MyAdvancedStruct {
inner: MyStruct,
advanced_a: AdvTypeA,
advanced_b: AdvTypeB
}
(or even simpler a new-type wrapper like struct MyAdvancedStruct(MyStruct)
).
It would be cool if we could easily propagate the fields from the inner structs to the outer struct; e.g. like this:
struct MyAdvancedStruct {
#[get_inner_pub = "field_a"] inner: MyStruct,
#[get = "pub"] advanced_a: AdvTypeA,
#[get = "pub"] advanced_b: AdvTypeB
}
which would create the getters field_a
, advanced_a
and advanced_b
for MyAdvancedStruct
.
AFAIK this shouldn't be too hard and I'd be willing to implement this if this is considered to be in the scope of this crate ๐
Hey @Hoverbear ๐, do you think it would be possible to release a new version which contains the latest features? Thank you in advance! ๐
For example
#[set = "pub"]
struct Hello {
message: String
}
would generate
impl Hello {
pub fn set_message(&mut self, value: String) -> &mut Self {
self.value = value;
self
}
}
It'd be nice to have something like this generated instead
impl Hello {
pub fn set_message<T: Into<String>>(&mut self, value: T) -> &mut Self {
self.value = value.into();
self
}
}
this would allow the user to set more, than just String like &str
, &String
or Cow<'_, str>
.
Maybe this could be enable by adding #[setter(into)]
to the struct
#[setter(pub, into)]
struct Hello {
message: String
}
For reference: derive_builder
support set_copy
#[derive(...)]
pub struct Foo {
#[set_copy="pub"]
bar: bool
}
that will expand like
impl Foo {
pub fn set_bar(self, bar: bool) -> Self {
self.bar = bar;
self
}
}
It would be convenient,
if for types [ui][8|16|32|64|128], usize, isize, f32,f64,bool
getset
will be generat getters that return value, not reference,
example:
#[derive(Debug, Default, Clone, Getters, Setters)]
struct Foo {
#[get]
#[set]
x: f64,
}
fn f() {
let mut f = Foo::default();
f.set_x(1.5);
let x: f64 = f.x();
}
I have a struct that uses the attribute #[serde(flatten)]
to share fields between multiple structs from an API.
For example, I have this:
#[derive(Deserialize, Getter)]
#[get = "pub"]
struct PartialChapterData {
id: u64,
language: String,
}
#[derive(Deserialize, Getters)
#[get = "pub"]
struct UserChapters {
user_id: u64,
created: u64, // Unix timestamp
chapters: Vec<PartialChapterData>,
}
#[derive(Deserialize, Getters)
#[get = "pub"]
struct DetailedChapter {
#[serde(flatten)]
chapter_partial: PartialChapterData,
volume: u16,
title: String,
}
fn main() {
let test_json = r#"{
"id": 1,
"language": "English",
"volume": 1,
"title": "The Chapter Title"
}"#;
let detailed_chapter: DetailedChapter = serde_json::from_str(test_json)?;
println!(
"Title: {}\nID: {}",
detailed_chapter.title(), // Does not panic
detailed_chapter.id(), // Panics with the error: error[E0599]: no method named `id` found for reference `&my_test_crate::v2::responses::chapter::DetailedChapter` in the current scope
);
}
Is there a correct method or workaround for this? I haven't tested the Setter
trait as I don't have a need for it.
Edit 1:
My workaround for now is to manually implement the setters in DetailedChapter
like so:
// ...
#[derive(Deserialize, Getter)]
#[get = "pub"]
struct PartialChapterData {
id: u64,
language: String,
}
#[derive(Deserialize, Getters)
#[get = "pub"]
struct DetailedChapter {
#[serde(flatten)]
chapter_partial: PartialChapterData,
volume: u16,
title: String,
}
impl DetailedChapter {
pub fn id(&self) -> &u64 {
&self.chapter_partial.id()
}
pub fn language(&self) -> &String {
&self.chapter_partial.language()
}
}
The other getters .volume()
and .title()
are generated from getset
without needing to re-implement them.
This is a really useful library that I've been using quite a bit. Most of the time I want all struct fields to be #[get = "pub"]
so being able to annotate a struct would be quite useful. I've come from the Java world where this can be done and there are also negating annotations that prevent getters and setters from being generated or made public, so some syntax like that may be useful.
For example
struct Hello {
value: Vec<usize>,
}
the value field will be set like any other (set_value(vec)
), but it'd be more useful to have something like this additionally;
hello.push_value(1);
hello.set_value(vec![1, 2, 3, 4]);
hello.push_value(5);
assert_eq!(hello.value, vec![1, 2, 3, 4, 5]);
The same could be done for other kinds of collections like HashMap
or BTreeMap
. (std::collections)
When #[getset(get_copy = "pub")]
is specified but CopyGetters
is not derived (only Getters
), the proc-macro doesn't give an error, instead rustc gives the error that foo.bar()
is not a method but a field.
Solution: The proc-macro could give an error. But I'd prefer another solution:
Remove CopyGetters
altogether, only have Getters
, such that get_copy
works when Getters
is derived. This makes much more sense IMO.
Or is there a reason to require the user to derive CopyGetters
/ any disadvantages when get_copy
is also allowed with Getters
?
To me it seems to be more verbose / requiring more typing than necessary, and I often forget to derive CopyGetters
.
Hey @Hoverbear wave, do you think it would be possible to release a new version which contains the latest features? Thank you in advance! ๐
Is there any current way to automatically implement getter/setter functions for specific traits? For example, I would like to have a Private
trait that relies on having a private()
getter:
use getset::{CopyGetters, Getters, MutGetters, Setters};
pub trait Private {
fn private(&self) -> &i32;
}
#[derive(Getters, Setters, MutGetters, CopyGetters, Default)]
pub struct Foo {
#[getset(get, set, get_mut)]
private: i32,
}
impl Private for Foo {};
fn main() {
let mut foo = Foo::default();
foo.set_private(1);
(*foo.private_mut()) += 1;
assert_eq!(*foo.private(), 2);
}
The above obviously fails unless I do something like:
impl Private for Foo {
fn private(&self) -> &i32 {
self.private()
}
}
The question is whether there is a way to specify in getset that private()
should be implemented for the trait Public
and avoid the extra boilerplate. And if not, is this something that could be implemented in the future?
@Boscop, I sent you an invite to join as a maintainer. Feel free to add your name to the crate metadata. ๐ You seem to care and have a vested interest in this, so I value your opinion on the future direction. I don't have your contact details so I resorted to this. :)
If you accept please add yourself to the crate metadata. I can add you to the crates.io project if you'd like to publish as well.
First of all, thanks for maintaining such a handy crate!
Right now I'm trying to use it implement functions from traits, so I wonder whether it's possible to put the getters/setters into impl
blocks? For example, I'd like to do:
trait HasFooSetter {
fn some_fn_that_modifies_foo(&mut self) {
self.set_foo("foo".to_owned());
}
fn set_foo(&mut self, foo: String) -> &mut Self;
}
#[derive(Setters)]
struct HasFoo {
#[getset(set = "pub impl=HasFooSetter")]
foo: String,
}
impl HasFooSetter for HasFoo {}
Please make all getters and setters #[inline(always)]
, to be sure they always get inlined :)
Getters don't make sense if their values are unused, and most likely implies a forgotten bug or a misunderstanding of the API.
I am not sure whether marking a function #[must_use]
breaks semver BC, but it would be healthy to add more #[must_use]
.
For example, one of my previous PRs was broken ๐ข and I didn't notice (yes, the master is broken, sorry). Continuous integration would allow you to catch such cases.
Are they possible with this crate, how?
E.g. if the field is a primitive type, I often want to copy it in the getter rather than return a ref.
Pretty please
Very new to rust, and think this library is really cool.
I wonder if it would be possible to have setters return a reference to self, such that you could chain setters, e.g.:
obj
.set_foo(2)
.set_bar(3);
Hi, I am a very happy user of this crate. And before anything else, I will say that the current feature set satisfies all my needs, although sometimes I am thinking that having "self-consuming" setters (fn setter(self, value) -> Self
) would be nice.
When reading comments in #84 and #87, I saw comments by @Hoverbear that "the features [this crate] offers and the way it does that are [not] very good". Could you or someone else please elaborate on why? Or perhaps say what to use instead (links would suffice)? I am genuinely curious, I may learn something new.
Since I am opening an issue, I will use it for the question about the plan for this crate. As I said, I am happy with the features that are already available. And I would be fine with knowing that the crate is in maintenance mode and no new, especially bigger features like "self-consuming" setters or support for Option<T>
fields, will be merged. But would be small maintenance PRs like fixing small bugs or bumping versions of dependencies accepted?
Might be worth considering to put the answer to the readme, too.
Thanks for this helpful tool!
It appears that the Getters
derive panics when encountering a raw identifier. The error was reproducible on master
as well as the most recently published crates.io version:
ยฑ cargo build
Compiling getset-bug v0.1.0 (/Users/pwoolcock/code/getset-bug)
thread 'rustc' panicked at '`"r#type"` is not a valid identifier', src/librustc_expand/proc_macro_server.rs:329:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: internal compiler error: unexpected panic
note: the compiler unexpectedly panicked. this is a bug.
note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports
note: rustc 1.44.0-nightly (f4c675c47 2020-03-19) running on x86_64-apple-darwin
note: compiler flags: -C debuginfo=2 -C incremental --crate-type lib
note: some of the compiler flags provided by cargo are hidden
error: proc-macro derive panicked
--> src/lib.rs:3:10
|
3 | #[derive(Getters)]
| ^^^^^^^
|
= help: message: `"r#type"` is not a valid identifier
error: aborting due to previous error
error: could not compile `getset-bug`.
To learn more, run the command again with --verbose.
and here's the code I used to generate this error:
use getset::Getters;
#[derive(Getters)]
pub struct Foo {
r#type: String,
}
Thanks for this awesome crate!
#[derive(Default, getset::Getters)]
struct A {
#[get = "pub"]
b: Option<B>,
}
#[derive(Default)]
struct B;
fn main() {
let a = A::default();
let b: Option<&B> = a.b();
}
error[E0308]: mismatched types
--> src/main.rs:12:25
|
12 | let b: Option<&B> = a.b();
| ---------- ^^^^^
| | |
| | expected enum `Option`, found `&Option<B>`
| | help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `a.b().as_ref()`
| expected due to this
|
= note: expected enum `Option<&B>`
found reference `&Option<B>`
Getters that return &Option<T>
(just like &Result<T>
, etc.) are not really useful, as they require getter user to pollute code with .as_ref()
everywhere. As alternative, having manual trivial getters like
impl A {
pub fn b(&self) -> Option<&B> {
self.b.as_ref()
}
}
is just another kind of boilerplate code pollution.
Can we do anything with this (and not some breaking change at the same time of course)?
I guess solution would likely require working with each such type separately (perhaps adding OptionGetters
and #[get_option = "pub"]
, etc.), but still this would make this crate more coherent for users.
While working on rust-bio/rust-bio#253, we noticed that cargo would complain about duplicate definitions if the struct-level annotation is used and a getter/setter is redefined elsewhere. Would it be possible to allow override of derived methods when struct-level annotation is used?
It would be nice to be able to write multiple getset attributes in one line, to reduce verbosity, but when I do (e.g. #[get = "pub", set = "pub"]
) the proc-macro panics..
Can you make it work, please? :)
It would be nice to have serde like syntax.
This make crate is more easy to use, because of many Rust users are already familiar with this syntax, or would be familiar in the near future.
The raw idea:
#[derive(Getset)]
#[getset(setters, getters)]
struct Foo {
#[getset(get_copy)]
f: i32,
#[getset(get_deref=&str)]
a: String,
#[getset(get_deref=&str)]
b: String,
#[getset(skip)]
c: usize,
}
I would like to derive fluent, or 'builder' style API
for my use-case I would prefer settings returning Self
, but for other use-cases returning &mut Self
is probably more appropriate. ideally both would be supported.
I'm using this crate a lot and it's making my struct definitions kinda verbose, so I propose this idea to reduce the verbosity:
Whenever a field has #[set]
, it should also automatically get a getter, and when it has get_mut it should automatically get a setter and getter, because get_mut already can be used as a setter but the actual set() should be generated for convenience. And we can assume that whenever a field has a setter, the author usually also wants it to have a getter as well. In those rare circumstances when that is not the case, the author can just write a manual setter instead of using the attribute.
What do you think?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.