6 ms·
Overall this looks nice, but I found myself stumbling over the ToSpan syntax: let span = 5.days().hours(8).minutes(1); It feels sort of weird how the firs
by sushibowl 2y ago
Overall this looks nice, but I found myself stumbling over the ToSpan syntax:
let span = 5.days().hours(8).minutes(1);
It feels sort of weird how the first number appears in front, and then all the other ones are function arguments. I suppose if you don't like that you can just write:
let span = Span::new().days(5).hours(8).minutes(1);
at the expense of a couple characters, which is not too bad.
- Galanwe 2y agoIf only Rust had named function parameters, you could write what is IMHO the most readable option: Span::new(days=5, hours=8, minutes=1)
- DemocracyFTW2 2y agocould you do that with `struct` / `record` fields? In JavaScript which doesn't have named function parameters either I often write functions with a single `cfg` parameter that are called like `f({ hours: 2, seconds: 53, })` which I find nice b/c it re-uses existing data structures.
- Galanwe 2y agoWell the beautiful thing about software engineering is that pretty much everything is possible, it essentially boils down to "but should you really"? :-)
- 4hg4ufxhy 2y agoKind of inefficient. Also it's less ergonomic since every struct is it's own type, so you need to have the signature on both sides.
- benmmurphy 2y agoIt should compile to the same because a struct passed by value is loaded into the registers the same as method arguments.
- devanl 2y agoIn Rust, you can't implicitly omit fields when instantiating a struct, so it would have to be a bit more verbose, explicitly using Rust's analog to the spread syntax. It would have to look something like: f({ hours: 2, seconds: 53, ..Default::default() }) The defaults could come from some value / function with a name shorter than Default::default(), but it would be less clear.
- estebank 2y agoAdding support for struct default field values would allow for - leaving some mandatory fields - reduce the need for the builder pattern - enable the above to be written as f(S { hours: 2, seconds: 53, .. }) If that feature ever lands, coupled with structural/anonymous structs or struct literal inference, you're getting everything you'd want from named arguments without any of the foot guns.
- kibwen 2y agoHas anyone ever proposed it? It's such a straightforward feature with such obvious semantics ("default field values are const contexts") and impossible-to-bikeshed syntax (`a: i32 = 42`) that I've been meaning to write up an RFC myself for around, oh, ten years now...
- steveklabnik 2y agonrc was poking at the problem in 2016 https://internals.rust-lang.org/t/struct-field-defaults/3412 https://internals.rust-lang.org/t/struct-field-defaults/3412 which led to this RFC https://github.com/rust-lang/rfcs/pull/1806 https://github.com/rust-lang/rfcs/pull/1806 It got postponed because it wasn't going to make it into Rust 2018: https://github.com/rust-lang/rfcs/pull/1806#issuecomment-327922562 https://github.com/rust-lang/rfcs/pull/1806#issuecomment-327...
- estebank 2y agoLast time I had this conversation[1] the main sticking point were around semantics of private fields, but I am convinced that the maximally restrictive version can be done (only pub fields can be optional) and maybe relaxed later after we get some real world experience. The other thing was about whether the default values needed to be const, but that makes sense as a restriction to me. 1: https://internals.rust-lang.org/t/pre-pre-rfc-syntactic-sugar-for-default-default/13234/74 https://internals.rust-lang.org/t/pre-pre-rfc-syntactic-suga...
- _flux 2y agoYes, that could've been lended almost as-is from OCaml, in particular as Rust doesn't have partial application so optional arguments would work out-of-the-box as well.
- berkes 2y agoAre named arguments on the roadmap somewhere? Or is it a won't-fix?
- _flux 2y agoI haven't seen anything for or against in actual roadmaps. But there is, of course, at least one pre-proposal: https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413 https://internals.rust-lang.org/t/pre-rfc-named-arguments/16... It doesn't think about optional arguments (but somehow does include overloading). And a bit in a related fashion, it doesn't permit for reordering calling arguments, which I consider a downside: > Reordering named arguments when calling > No it is not possible. Just like unnamed arguments and generics, named arguments are also position-based and cannot be reordered when calling: register(name:surname:) cannot be called as register(surname:name:). > Reordering them at the definition site is an API break, just like reordering unnamed arguments or generics is an API break already. The rationale for this expressed in the comments says it's incompatible with overloading, but to me I don't see why named arguments and overloading should go hand-in-hand—or, indeed, how desirable overloading is in the first place. Or why should overloading be able to overload that kind of scenario. The other reasons for this don't seem really problems at all. > fn func2(pub name: u32, name hidden: u32) { /\* ... */ } > fn func3(name hidden1: u32, name hidden2: u32) { /* ... */ } > func2 and func3 could work in theory: named arguments as proposed in this RFC are position-based and their internal names are different: just like two arguments can have the same type without ambiguity, those functions could be allowed. Maybe there are technical reasons that are simpler when considering type compatibility between function types that have or don't have labeled arguments? Seems the proposal has misunderstood something about OCaml labeled arguments when placing it under https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413#mandatory-and-ordered-named-arguments-61 https://internals.rust-lang.org/t/pre-rfc-named-arguments/16... , though. In addition the proposal doesn't seem to have a neat syntax for forwarding named parameters, like in constructing records you can just fill in a field called foo by mentioning its name by itself—or, like in OCaml you can have let foo ~bar = bar + 1 let baz ~bar = foo ~bar let main () = baz ~bar:42 If it used the .-prefix as mentioned as an idea elsewhere, then this too could be naturally expressed. Maybe there are other ideas how to go about the labeled arguments, though that one seems pretty well thought-out. One thing I've enjoyed with Python (and Mypy) is the ability to require the caller to use named arguments with the asterisk marker in the parameter list. This idea is mentioned in the proposal.
- nurettin 2y agoI'm all for named parameters. C++ is sorely lacking that feature as well. Currently using vs code with C++, I like how it handles the missing language feature by adding a grayed out parameter name before the value for function calls and initializers. Maybe there is something like that for rust.
- School-Cotton 2y agoYes, editors can be configured to do the same thing for rust.
- hypeatei 2y agoThese are called "inlay hints" and exist for most editors/languages.
- tomas789 2y agoI agree with that. In my thought process is to specify what I’m doing and only then some details. This is the other way around. When reading the code, it would be better to see that I’m dealing with span at first.
- DemocracyFTW2 2y agoI stumbled over use jiff::{Timestamp, ToSpan}; fn main() -> Result<(), jiff::Error> { let time: Timestamp = "2024-07-11T01:14:00Z".parse()?; I seem to remember Rust does that thing with interfaces instead of classes, is it that? How come I import a library and all of a sudden strings have a `parse()` method that despite its generic name results in a `Timestamp` object? or is it the left-hand side that determines which meaning `str.parse()` should have? What if I have two libraries, one for dates and one for say, Lisp expressions that both augment strings with a `parse()` method? Why use this syntax at all, why not, say, `Timestamp.parse( str )`? I have so many questions.
- 0x457 2y ago> I have so many questions. Not being snarky, but I suggest starting by reading at least a little about traits? None of your questions are really about this library - it's just FromStr and an orphan rule.
- DemocracyFTW2 2y agoTo make it clear, I didn't want to be the snark either. Just wondered about the usual things like "locally detectable semantics" and so on. I still think `Library.method( argument )` wins over `argument.method()` for the simple reason that the former has `Library` explicitly mentioned. Also, `door.open()`, really? I think `strongman.open( door )` is just that much clearer, flexible and explicit.
- 0x457 2y agoAgree to disagree. I always like expressiveness of Ruby, so `2.days` and `2.days()` look totally normal to me. > `strongman.open( door )` is just that much clearer, flexible and explicit. Where the strongman came from? Entirely different semantics.
- Tigress8780 2y agoRust will determine what `parse` does based on the inferred return type (which is being explicitly set to `Timestamp` here). This is possible when the return type has `FromStr` trait.
- frereit 2y agoI agree. Personally, I'd prefer let span = 5.days() + 8.hours() + 1.minutes();
- csomar 2y agoI wonder if OP will accept a PR for such a change. Your proposal is much readable and flexible (it's not clear from the docs if you can add random time ranges together). Plus, you'll be able to create your own ranges like `1.decade` or `1.application_timeframe` and add/subtract them.
- mijoharas 2y agoHave you checked the API to see if that works? I imagine it does.
- mutatio 2y agoLooks like it should be supported: https://docs.rs/jiff/latest/jiff/struct.Span.html#impl-Add%3CSpan%3E-for-%26Zoned https://docs.rs/jiff/latest/jiff/struct.Span.html#impl-Add%3...
- wging 2y agoThat isn't an implementation of addition between Spans and other Spans. It looks like there isn't one in the library right now. `impl<'a> Add<Span> for &'a Zoned` means a borrow of Zoned is on the left hand side, and a Span on the right. So it says that if z is a Zoned (not a Span) and s is a Span, you can do `&z + s` to add a span to a Zoned. There are a bunch of implementations there, DateTime + Span, Date + Span, Time + Span, Offset + Span. All with Span on the right, but none for Span + Span (nor Span + &Span, or &Span + &Span, ...).
- burntsushi 2y agoThis is correct. You can't do a `span1 + span2`. You'd have to use `span1.checked_add(span2)`. The main problem I had with overloading `+` for span addition is that, in order to add two spans with non-uniform units (like years and months), you need a relative datetime. So `+` would effectively have to panic if you did `1.year() + 2.months()`, which seems like a horrific footgun. It would be plausible to make `+` for spans do _only_ component wise addition, but this would be an extremely subtle distinction between `+` and `Span::checked_add`. To the point where sometimes `+` and `checked_add` would agree on the results and sometimes they wouldn't. I think that would also be bad. So I started conservative for the time being: no `+` for adding spans together.
- Sharlin 2y agoYeah, or there could simply be a `days()` free function (and equivalents of the other methods too). No need for struct constructors to be associated functions.
- creata 2y agoI haven't tried it (so I'm sorry if it's wrong or not what you're talking about) but can't you get a freestanding days function by use jiff::ToSpan::days;
- the_mitsuhiko 2y agoYou cannot import trait methods as free standing functions. I'm not sure if there was a discussion about making this a possibility but it's definitely not something you can do today.
- creata 2y agoOh, sorry about that then.
- dathinab 2y agomultiple discussions happened for this and I don't quite remember the outcome. But it's much less simple then it seems. Because `use Trait::method` would not be one (potential generic) method but a group of them so it would be it's own kind of thing working differently to free functions etc. Furthermore as generics might be on the trait you might not be able to fill them in with `::<>` and even if you fill them in you also wouldn't be able to get a function pointer without having a way to also specify the type the trait is implemented on. All of this (and probably more issues) are solvable AFIK but in context of this being a minor UX benefit its IMHO not worth it, 1. due to additional compiler complexity but also due to 2. additional language complexity. Through maybe it will happen if someone really cares about it. Anyway until then you can always define a free function which just calls the method, e.g. `fn default<T: Default>() -> T { T::default() }`. (Which is probably roughly how `use` on a trait method would work if it where a thing.)
- gpderetta 2y agoCan you do 5.days() + 8.hours() + 1.minutes()?
- coldtea 2y agoI like your version's consistency. The original looks like something Ruby would do.
- michaelcampbell 2y agoOnly in your memory, I think. > let span = 5.days().hours(8).minutes(1); # "original" Actual ruby (with Rails extensions; stock ruby doesn't do this) irb(main):001> now = Time.now => 2024-07-23 08:31:53.656455305 -0400 irb(main):002> now + 4.days + 8.minutes + 2.hours => 2024-07-27 10:39:53.656455305 -0400 So the version of his that you like for "consistency" is far more closely aligned to what ruby/rails _actually_ does.
- waterhouse 2y agoOr 0.days(5).hours(8).minutes(1)?
- tempodox 2y agoGreat, that gives us 0-days.
- bestouff 2y agoOr even better: let span = Span::days(5).hours(8).minutes(1);
- burntsushi 2y agoI believe this could actually not work. You would need a `fn days(n: i64) -> Span` and a `fn days(self, n: i64) -> Span` to exist at the same time, which Rust does not allow.