Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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
Add methods in
Varargs
to perform arguments validation and type conversion #892Add methods in
Varargs
to perform arguments validation and type conversion #892Changes from all commits
94c3738
f2e2823
5ae565a
b4fc5aa
1ebebb2
File filter
Filter by extension
Conversations
Jump to
There are no files selected for viewing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe mention that this is for optional parameters?
Also, we should probably clearly distinguish parameters (at declaration site) and arguments (at call site).
For
get
on the other hand, you could mention:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would reserve
#[must_use]
for situations, where both:I don't think any of the uses in this file qualifies for these criteria. Simple getters or even
new()
do generally not fall in that category, otherwise we'd have to annotate half the library with#[must_use]
.Some examples where we used it:
fn test_something() -> bool
in Godot integration testsMethodBuilder
(struct attribute)done()
invocation.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example, code like this.
This would be unintended behavior for the person who wrote this, so it makes sense to warn them.
The standard library recently did that. So even
godot-rust
will have to do so by the time the version reaches1.0.0
.Actually, when I auto-generated the getter, it just generated the
#[must_use]
as well, which I didn't intend. 🙃There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's actually a good example!
offset
in English can be understood as both a verb and noun.However, I think we agreed that storing the iterator state in
Varargs
(and thus the offset) is not a great idea -- so I don't see why we add more methods likeoffset_index()
? That only means more breaking code once we move away from it.Interesting, wasn't aware of the standard library doing that, as
#[must_use]
is not part of the documentation. It seems like rust-lang/rust#89692 was a big change. I've also seen the guidelines, but they're not very explicit on these border cases; they basically say "if it's legitimate to ignore the return value, don't add it" which is, well... very obvious.But I don't understand why godot-rust would have to follow the standard library once it's 1.0.0? While the standard library can be a good inspiration for other libraries, the requirements are quite different -- it has a vast number of users across all audiences and must generally be very conservative regarding compatibility, potential bugs, safety, etc. As an example, we will likely deviate from some "standard practices" because godot-rust is such a FFI-heavy library which needs to deal with threading and unsafety out of its control. There's very little guidance about this use case, even in the Nomicon. So I don't see the standard library as "the one true library which is right in every sense". We definitely have the freedom to define our own conventions within certain bounds.
Apart from that, it would be nice if changes to code style (which affect the whole library) would take place outside of feature PRs, otherwise we end up with an inconsistent state and an unclear "set of rules". That said, I don't think
#[must_use]
a priority for godot-rust right now, let's get some other open issues solved first 😉That's surprising, 've never seen that! Out of curiouity, which IDE do you use?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need these
From
impls? I don't think it's a big issue to writeVarargsError::ArgumentTypeError(my_error)
instead ofmy_error.into()
🤔See also my comment in the other PR #886 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of this.
Is this okay?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, absolutely! It's anyway encapsulated in a macro 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Errors in godot-rust typically don't have rich APIs, e.g.
FromVariantError
is simply an enum without methods.FromVariantError::InvalidLength
in particular is a good inspiration for this implementation:I think we could simply make the fields public here, and use a more similar naming:
Then, we would not need the 3 methods
passed()
,expected_min()
andexpected_max()
-- it's anyway not clear if returningusize::MIN
/usize::MAX
is intuitive for the user -- and withBounds
, the user can work with a higher-level API.In 99% of the cases, a user would not even need to access these values programmatically, but simply print the error message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TLDR: let's keep rarely-used APIs as simple as possible, with only as much code as necessary.
We can always add more features in the future if there is actual demand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case, the field becomes mutable.
Which is the better choice? 🤔
If it is an API that users can implement on their own, there is no problem without it, but since it is impossible to implement, this is major limitation for users.
Also, just because there are more lines of code does not mean that they are more complex.
Will you investigate demand later? I think that would be more labor intensive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, just like the
FromVariantError
enum.What do you mean here? We expose all the relevant info with
len
,expected_min
andexpected_max
being public. We can also keepDisplay
for convenience. The user can do everything, no?First, a user expects similar functionality to have similar APIs.
FromVariantError
variants provide mutable, publicly accessible fields (implied by being an enum).Now
VarargsError
is completely different: it uses tuple-like enumerators instead of struct-like ones, and its external structs are encapsulating their fields. This inconsistency exists despite both APIs serving an identical purpose.Regarding the demand part of your question -- I'm a big fan of "keep it simple". No one ever demanded highly encapsulated error APIs. Most people don't even care about the error -- they
unwrap()
and if there's a panic, it's a bug to be fixed. At most, people may output the error message. I guess we could even useanyhow::Error
to throw a string inside, and most users would still be happy. But we're already providing much more than that -- the user can differentiate precisely which error happened and react to it in a very specific way, if necessary. I don't think anything beyond this (such asFrom
, or separate struct types, or encapsulation) adds real value to the library.If I am wrong in my opinion, people have the option to use the issue tracker. I personally believe it's much less labor-intensive to start with little code and expand when needed, rather than put a lot of up-front effort for a corner-case API and make assumptions how this is going to benefit everyone. And that's without even mentioning maintenance of the code itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Serious question: why don't we go with this? It's like
FromVariantError
does it.