[HN Gopher] The Plan for the Rust 2021 Edition
       ___________________________________________________________________
        
       The Plan for the Rust 2021 Edition
        
       Author : caution
       Score  : 238 points
       Date   : 2021-05-11 15:09 UTC (7 hours ago)
        
 (HTM) web link (blog.rust-lang.org)
 (TXT) w3m dump (blog.rust-lang.org)
        
       | Flex247A wrote:
       | Really excited to see the syntax becoming more consistent!
       | 
       | I am looking forward to master Rust in the coming 4-5 years.
        
         | axiosgunnar wrote:
         | Since you said ,,master" you are probably already somewhat
         | proficient, but if not, I can not recommend enough to just try
         | Rust out for some project one day. It literally took me 2 days
         | from writing my first line of Rust to having a central
         | component of our pipeline rewritten in Rust and running _in
         | production_ and I enjoy every minute of writing Rust, since
         | (after you win the battle with the borrow checker) everything
         | ,,just works".
        
           | afavour wrote:
           | I've written a fair amount of Rust and my beginner experience
           | was not as positive as yours. It took me a very long time to
           | fully deal with things like the borrow checker.
           | 
           | My personal recommendation to anyone just writing code as a
           | learning exercise is that they start with using Rc<> wherever
           | a borrow checker-related issue arises. Then you can get used
           | to the rest of Rust. Once you feel a lot more proficient you
           | can go back and start replacing Rc<> with references (and you
           | might find that you don't even need to in many instances
           | anyway)
        
             | throwaway894345 wrote:
             | This is interesting. I did `clone()` everywhere rather than
             | `Rc<>` I wonder how these approaches compare?
        
               | steveklabnik wrote:
               | On most things, clone() will make a full copy. If your
               | type is wrapped in Rc<T>, then calling clone() on it will
               | increase a reference count. So it ends up being cheaper.
               | If you were mutating things, though, you'll need extra
               | stuff in the Rc<T> case, though. If you're mutating in
               | the regular case, you'd be changing the copies, of
               | course, so the originals wouldn't change.
        
               | Klasiaster wrote:
               | For the read-only case it's the same except for a larger
               | overhead of the cloning.
               | 
               | But for the case where the data is modified, Rc often
               | goes with RefCell as Rc<RefCell<X>> and allows in-place
               | mutation called interior mutability. This means that the
               | other code that got the copy of the Rc will observe the
               | write effects.
        
           | tick_tock_tick wrote:
           | I would never trust something anything I've only been exposed
           | to for two days in production. The chance of some kind of
           | subtle error is wayyyy too high.
        
           | SatvikBeri wrote:
           | Two days is fast! What background were you coming from - did
           | you have previous experience with non-gc languages?
        
             | axiosgunnar wrote:
             | Actually it was two weeks - I mistyped. And no, I did not
             | have non-gc-language experience but tbh Rust feels like a
             | gc-language to me? I don't "feel" like I'm writing C code
             | you know?
        
               | brabel wrote:
               | holy shit, I was feeling like an idiot to know someone
               | can actually get to grips with Rust basics in just 2 days
               | :D took me more like two months to stop struggling
               | constantly on every line I wrote!
        
               | bombela wrote:
               | I have pushed people to learn Rust. And I noted that the
               | more experience in software design a person has, the
               | easier it is for them to truly internalize the borrow
               | checker.
               | 
               | I suspect this is because you naturally get to an
               | instinctive form of ownership model similar to Rust.
               | Since it is a really good way to reduce complexity. Rust
               | then, merely formalizes and name it for you.
        
           | chrismorgan wrote:
           | Battles with the borrow checker are seldom _won_. Victory is
           | conceding that the compiler knows better than you and fixing
           | your design.
           | 
           | (I'm serious about this, as a user for eight years and casual
           | trainer for several. The fact of the matter is that when the
           | borrow checker complains, it's _right_ to complain,  >99.99%
           | of the time, even if you had thought carefully about it and
           | were _sure_ you got it right and that the compiler's
           | complaint was unnecessary.)
        
             | thethirdone wrote:
             | I would lower the percentage it is right to complain about
             | to only 99.9% (without non-lexical lifetimes it is probably
             | only right 99% of the time). I have encountered like 3
             | cases where the borrow checker complained and forced me to
             | make a worse design. I think I have seen more than 1000 and
             | less than 10,000 borrow checking errors.
        
           | throwaway894345 wrote:
           | I also like writing Rust, but "After you win the battle with
           | the borrow checker" is doing a lot of work. Those battles
           | often crop up unexpectedly and sometimes they're easily
           | resolved and other times they require reworking your
           | architecture--it's very hard to estimate how long it will
           | take. Of course, there are escape hatches--you can clone
           | excessively, but it's not all peaches and cream.
        
             | whatshisface wrote:
             | About 80% of my battles with the borrow checker eventually
             | resolve with me realizing the code I'm trying to get
             | working could lead to a bug in a case I hadn't considered.
             | Of course, I could just be an abnormally sloppy programmer.
        
               | throwaway894345 wrote:
               | In my experience, borrow checker errors would only be
               | "bugs" in a program that doesn't have a garbage collector
               | or if there is shared memory parallelism involved. Put
               | differently, if you slapped a borrow checker on Go and
               | your program was single-threaded, (I posit but welcome
               | correction) borrow-checker errors would probably not be
               | uncovering many bugs.
        
               | whatshisface wrote:
               | Not for me, it's usually something related to aliasing or
               | iterator invalidation.
        
               | dj_mc_merlin wrote:
               | Yes, the point of the borrow checker is memory safety
               | without GC.
        
       | asimpletune wrote:
       | I love how subtle and thoughtful these changes are. Rust isn't
       | only a great language, it's a model of how technology can be
       | built.
        
         | throwaway894345 wrote:
         | Agreed. And I'm not sure if this is more or less precise than
         | "model", but I often think about Rust as a _process and
         | community_ for developing a programming language, rather than
         | as a programming language itself. This seems related to your
         | 'model' remark.
        
         | brundolf wrote:
         | It's amazing how perfectly they're able to balance pragmatism
         | with idealism/principles. For me that might be the defining
         | trait of this language and community; most languages swing too
         | hard in one direction or the other.
        
       | api wrote:
       | These seem pretty minimal. Most of the code I can recall writing
       | or have seen would require no changes or maybe a one liner here
       | and there.
        
         | ckok wrote:
         | But that's the right way to do it really... I remember an
         | obscure thing in c# when var was introduced that if you have a
         | type called 'var' in scope, type inference doesn't work and it
         | uses that type instead. This is the way new features should be
         | introduced.
        
         | raphlinus wrote:
         | I think that's a good thing. People do complain about the
         | overly high rate of evolution of Rust, but I think that was
         | much more true in previous years than today (not least because
         | a lot of projects needed to depend on nightly).
         | 
         | Also, most of these changes enable new things, and are only in
         | the edition because of some low chance of breaking existing
         | code. The 2018 edition had some major changes, so 2018 code
         | looks pretty different - the ? operator, big changes to module
         | use, impl/dyn Trait, and more. I suspect that most code bases
         | you won't be able to tell whether it's 2018 or 2021 without
         | looking more closely.
        
           | chrismorgan wrote:
           | I expect a common complaint to be "why isn't [].into_iter()
           | working in my tests? --oh, it's 2018 edition", though this
           | can be mitigated by a specialised error message which didn't
           | exist last week when I first learned about this matter.
        
         | nindalf wrote:
         | The 2018 edition came with tools to automate the migration. I
         | imagine most of these changes will be fully automated as well.
        
       | ebingdom wrote:
       | I love Rust, but one thing that seems like a glaring design flaw
       | is that adding a new trait implementation can change the behavior
       | of existing code. This is very counterintuitive, because it seems
       | like a purely additive change. In my opinion, the convenience of
       | auto-dereferencing "as much as possible" to make method call
       | syntax magically work through the indirection of references is
       | not worth the lack of stability guarantees that it causes. This
       | isn't a normal kind of breakage where the compiler points out
       | what code you need to fix; this is a much worse kind of breakage
       | where you might not even realize your code now behaves
       | differently. In my opinion, the notion of "editions" is not an
       | acceptable solution to this.
       | 
       | For a language that prioritizes safety, there are a surprising
       | number of gotchas in Rust (another example is the large number of
       | partial functions in the standard library).
        
         | Arnavion wrote:
         | A lot of `expr.expr` would need to become `(&expr).expr` or
         | `(&mut expr).expr` if auto-dereferencing wasn't a thing. C++
         | has `.` and `->` but Rust only has `.`
         | 
         | (To be clear, what's happening with `array.into_iter()` is
         | unsize coercion, not auto-dereferencing. `[T; N]` does not impl
         | `Deref<Target = [T]>`. But the point is the same.)
        
           | jeltz wrote:
           | What if borrow and deref were postfix operators? E.g
           | expr&.expr? That way it would be explicit without parenthesis
           | soup.
        
           | ebingdom wrote:
           | I think you have it reversed. `expr.expr` would need to
           | become `(*expr).expr` if we didn't have auto-dereferencing.
        
             | Arnavion wrote:
             | struct S;              impl S {             fn foo(&self) {
             | }         }              let s = S;         s.foo();
             | 
             | That compiles today. Your proposal would require it to be
             | `(&s).foo()`
        
               | a1369209993 wrote:
               | No, IIUC, the sematics is (approximately):
               | a.foo(b,c);       vvvv       typeof(a)::foo (??? a,b,c);
               | vvvv       A::foo /* takes &A */ (/*so this is:*/&
               | a,b,c);       vvvv       A::foo(&a,b,c);
               | 
               | Auto-derefencing is:                 a.foo(b,c)
               | vvvv // a is &X, so replace it with (*a)
               | (*a).foo(b,c) // wrong; should be a.foo(b,c); we called
               | vvvv  // a method on the pointer, not what it points to
               | X::foo(???(*a),b,c) // should be A::foo(???a,b,c)
               | // continue as above
               | 
               | Auto-derefencing changes which type the method is looked
               | up relative to, not just how the object is passed to it.
        
               | Arnavion wrote:
               | Yes yes, there's auto-ref, auto-deref, deref coercion,
               | unsize coercion, ... All of them are forms of "`lhs.rhs`
               | tries to look up a different type to apply the `rhs` to
               | vs what type `lhs` actually has," or more generally "I
               | wrote an expr of type T but the compiler treats it as an
               | expr of type U for :reasons:"
               | 
               | I called it "auto-dereferencing" because that's what OP
               | used when talking about `[].into_iter()`, and then
               | clarified that it's actually an unsize coercion, not
               | auto-deref.
        
               | a1369209993 wrote:
               | > All of them are forms of "`lhs.rhs` tries to look up a
               | different type to apply the `rhs` to vs what type `lhs`
               | actually has,"
               | 
               | Nope, it uses the same type (A), just blindly adds a
               | operator to the argument based on which method is called.
               | You could just as easily have `a.foo()` -> `A::foo(++a)`
               | instead; it's just[0] that adding `++` would be largely
               | useless.
               | 
               | 0: You'd also need a explict annotation on the
               | declaration of foo, but that's a convenience issue.
        
               | pcwalton wrote:
               | In other words, there is auto-ref in addition to auto-
               | deref, and auto-ref causes the same issues of possibly-
               | surprising method resolution. As I mention above, auto-
               | deref is just one way that methods could resolve to the
               | "wrong" implementation, and there would be no solution
               | that I can see other than to remove dot entirely, which
               | isn't viable.
        
         | steveklabnik wrote:
         | Static typing helps significantly here, though obviously not
         | completely. Often, the failure mode is a compiler error, not
         | silent code changes. Note the example of TryFrom; the failure
         | mode isn't that your program is now running a different
         | implementation, but that, because there are now two possible
         | implementations, it becomes ambiguous which is selected and a
         | compile-time error results.
         | 
         | Same with the array change, the failure mode was breakage.
         | While in this case, the method's names, arguments, and argument
         | types are identical, the return type is different, which means
         | other code expecting a certain type now has a different type.
         | For that to silently change behavior, it would also need to
         | match up on everything on the return type as well.
        
         | pcwalton wrote:
         | > I love Rust, but one thing that seems like a glaring design
         | flaw is that adding a new trait implementation can change the
         | behavior of existing code. This is very counterintuitive,
         | because it seems like a purely additive change.
         | 
         | > In my opinion, the convenience of auto-dereferencing "as much
         | as possible" to make method call syntax magically work through
         | the indirection of references is not worth the lack of
         | stability guarantees that it causes.
         | 
         | Auto-deref is just one way of many that the dot operator can
         | resolve to an unintended method. This problem would still exist
         | even if auto-deref were removed. What you're really objecting
         | to is the existence of the dot operator at all. I think the dot
         | operator pulls its weight, given how often it's used: I
         | encourage you to check out Simon Peyton-Jones' thoughts on "the
         | power of the dot" [1].
         | 
         | [1]: https://www.microsoft.com/en-us/research/wp-
         | content/uploads/...
        
       | sodality2 wrote:
       | I am actually excited to learn rust as I try to learn lower level
       | languages. Not so for any other language.
        
         | fastasucan wrote:
         | Same for me. I have no idea why, but it excites me to learn
         | rust. Maybe because it have a so obvious source to learn from
         | (The book), and that it still is fairly a new and "limited"
         | language? I can't wait to get some major work-hurdles out of
         | the way so I can go through the book. Not sure if I'll use Rust
         | that much professionally, but my experience so far is that I
         | learn so much about programming while I am learning it.
        
           | tvb12 wrote:
           | I thought this post was about the book, rather than the
           | language. The book is supposed to be up to date for Rust
           | 2018. I suppose there's probably no need to wait for the book
           | to catch up to the language.
        
             | steveklabnik wrote:
             | We will update the book, but only after the changes are
             | finalized :)
        
         | tdhz77 wrote:
         | I'm with you, but I wonder if it's because I get a lot of my
         | tech pulse from hacker news and the community for right or
         | wrong seems so bullish.
         | 
         | In my experience it was cool. I was impressed with the speed. I
         | tried to make a cryptocurrency (non-distributed) and that was
         | cool.
         | 
         | I don't think in low level yet, but I'm trying to figure it
         | out.
        
       | nynx wrote:
       | This doesn't require an edition change, but I'm really looking
       | forward to custom, per-collection allocator support. It's in
       | nightly right now and very nice to use.
        
         | swsieber wrote:
         | There's been a lot of attention given to the api since the
         | roll-out: https://internals.rust-lang.org/t/is-custom-
         | allocators-the-r...
         | 
         | I'm not sure if the changes discussed there are going to be
         | rolled in or not. It's certainly an interesting thread.
        
           | nynx wrote:
           | Yeah, I've been following that thread. The storage idea is
           | interesting but I don't think it's really necessary. The main
           | thing it would enable is inline allocators that can be stored
           | inside the collection, as opposed to the collection storing a
           | reference to the stack allocation. The cases where that's
           | necessary, one can use the stackvec or arrayvec crates, which
           | are more optimized than a generic inline allocator would be
           | anyhow.
        
       | floatboth wrote:
       | > Starting in Rust 2021, closures will only capture the fields
       | that they use
       | 
       | Holy shit yes. Thank you thank you thank you. This fixes my
       | biggest annoyance so far!
        
       | markwisde wrote:
       | "We can't add tryinto to the prelude, as a solution we're adding
       | tryinto"
       | 
       | What?
        
         | steveklabnik wrote:
         | You cropped out the relevant part:
         | 
         | > As a solution, Rust 2021 will use a new prelude.
         | 
         | That is, we cannot add it into the existing prelude. But we
         | could have a new prelude, tied to the new edition.
        
           | Animats wrote:
           | Shouldn't more of those prelude items be in "core", rather
           | than "std"? Many involve neither I/O nor allocation.
        
             | 3836293648 wrote:
             | Everything in core is also in std. You only link to
             | anything in core in a no_std context (or if you do it
             | explicitly for some reason)
        
             | TheCoelacanth wrote:
             | All of the new items are in "core", e.g. https://doc.rust-
             | lang.org/core/convert/trait.TryInto.html
        
           | markwisde wrote:
           | I don't see how this solves the issue of colliding with a
           | user-provided tryinto
        
             | msbarnett wrote:
             | It solves the issue by not breaking any existing code that
             | doesn't explicitly opt in to the new edition.
        
       | Traster wrote:
       | Man I feel like an idiot. I read the first paragraph and I'm like
       | "that's just versions" and then I read the next few paragraphs
       | and I'm like "Yeah but your dependencies are going to screw you".
       | 
       | I'm not sold on the raw identifier syntax and we'll see if the
       | changes are actually as good as they claim, but in general this
       | seems like a great way of doing language 'versions'.
       | 
       | Rust is definitely learning from the past and I'm really
       | interested see what we learn from how they fuck up too
        
         | steveklabnik wrote:
         | We've already done one of these! This is the second
         | introduction of a new edition, and therefore, our third
         | overall: 2015, 2018, and now 2021. If you want to see how it
         | works, you can create a new project, which defaults to Rust
         | 2018, and depend on https://crates.io/crates/semver, which is
         | on Rust 2015, and see that interop works just fine!
         | 
         | Yeah don't worry we'll fuck up for sure, haha. One could argue
         | we've made a few of those already...
        
       | RcouF1uZ4gsC wrote:
       | > However, note that Rust is a project run by volunteers. We
       | prioritize the personal well-being of everyone working on Rust
       | over any deadlines and expectations we might have set. This could
       | mean delaying the edition a version if necessary, or dropping a
       | feature that turns out to be too difficult or stressful to finish
       | in time.
       | 
       | What a refreshing take! One of the things I have admired about
       | Rust is the effort from the start to build a healthy, welcoming,
       | non-toxic community, and to prioritize personal well-being.
        
         | kbenson wrote:
         | Yeah, a lot of communities work this way implicitly, but being
         | explicit about it likely has some nice mental health benefits
         | in itself, as even if most people people are totally
         | understanding, sometimes the person themself needs it
         | explicitly said that it's okay for them to just back off for a
         | bit and they aren't letting everyone down. Sometimes it's our
         | own expectations and beliefs that drive us the hardest when we
         | need a break.
        
       | yakubin wrote:
       | One thing that I repeatedly bump into in Rust is the oddity of
       | multi-argument functions. To explain the problem, let's look at
       | another language for a moment: in SML there is no such thing as
       | multi-argument functions per se. There are two ways to write them
       | anyway:
       | 
       | * the function's argument is a tuple (this is the more common
       | way, not only in SML, but also how it's done most commonly in
       | maths);
       | 
       | * currying.
       | 
       | I'm not a fan of currying, because it singles out the first
       | argument over the others. However, the first approach (tuples) is
       | very ergonomic, because now you can pass the return value of one
       | function as an argument to a "multi-argument" function. One
       | application of that is function composition. But another is a
       | simple map. I've lost count of how many times in Rust I had an
       | "o: Option<(A, B)>" and a "f: fn(A, B) -> C" and couldn't call
       | "o.map(f)"; instead the programmer is forced to write more noisy
       | code with lambdas. And lambdas sometimes don't play well with the
       | borrow checker for mysterious reasons (even those that don't
       | capture anything).
       | 
       | Also, I think it would be nice if the compiler generated named
       | discriminants for every enum. Currently, "std::mem::discriminant"
       | returns an opaque "std::mem::Discriminant<T>" (which doesn't have
       | a name). In order to compare discriminants I need to create a
       | full-blown enum value and extract the discriminant from it later.
       | I've worked around this problem by using the "strum" crate, which
       | has a macro to produce another (plain) enum, but having the
       | discriminant type be named "std::mem::Discriminant<T>" (to signal
       | the connection to the original enum) _and_ have named variants at
       | the same time would be the best of both worlds.
       | 
       | EDIT: If I knew more about possible breakage caused by my first
       | suggestion, I could probably start working on an RFC. However, I
       | feel I don't have enough knowledge about Rust to anticipate that.
       | 
       | For my second suggestion I think I'm going to try to propose
       | that. I'm reading the docs on the process now.
        
         | brabel wrote:
         | > the function's argument is a tuple (this is the more common
         | way, not only in SML, but also how it's done most commonly in
         | maths);
         | 
         | The Ceylon programming language worked that way[1] and it was
         | really cool to be able just what you wanted: `o.map(f)`.
         | 
         | It's a shame that other languages didn't seem to pick up this
         | idea... I've no idea if this could be made to work in Rust
         | though (given Rust has no GC and it would go against its
         | philosophy to allocate parameters like this on the heap just to
         | create tuples - maybe the compiler could get rid of the actual
         | tuples altogether?).
         | 
         | [1] https://ceylon-lang.org/blog/2012/12/21/tuples-and-
         | functions...
        
           | terhechte wrote:
           | Swift had this feature until version 2.3 it was removed in
           | 3.0 because it added a lot of complexity and seemingly
           | confused users. I'd search for a link but I'm on mobile
        
           | anderskaseorg wrote:
           | Even though Ceylon uses tuple types to represent parameter
           | lists, a function accepting multiple arguments (with the
           | parameter list represented by a tuple: Callable<Void,
           | [Integer, String]>) is still different from a function
           | accepting a tuple of multiple fields (with the parameter list
           | represented by a tuple of a tuple: Callable<Void, [[Integer,
           | String]]>). You can't just call one as if it were the other.
           | Ceylon works the same way as Rust here, as far as I can tell.
           | 
           | BTW, for Rust beginners who might be concerned that o.map(f)
           | is somehow a difficult problem, the solution in Rust is
           | simple: o.map(|(a, b)| f(a, b)).
        
           | estebank wrote:
           | Rust tuples are on the stack, to have them on the heap you
           | need to explicitly use Box.
           | 
           | Funnily enough, internally this is how closures parameters
           | are represented internally, as a single argument that is a
           | tuple of all its parameters.
        
         | Arnavion wrote:
         | >I've lost count of how many times in Rust I had an "o:
         | Option<(A, B)>" and a "f: fn(A, B) -> C" and couldn't call
         | "o.map(f)"
         | 
         | Non-serious solution:                   #![feature(fn_traits,
         | unboxed_closures)]              pub fn foo<Args, C>(o:
         | Option<Args>, f: impl FnOnce<Args, Output = C>) -> Option<C> {
         | o.map(|v| f.call_once(v))         }
         | 
         | https://doc.rust-lang.org/stable/std/ops/trait.FnOnce.html
         | 
         | "Non-serious" because it won't be stable any time soon, and
         | it's not as concise as `o.map(f)` anyway.
         | 
         | More seriously, I don't think `o.map(f)` will ever happen,
         | because it'll be backward-incompatible in the case of `o:
         | Option<(T,)>` and `f: <U>impl FnOnce(U)` - `U` used to be
         | `(T,)` and now would be `T`. Something like `o.map(spread(f))`
         | might work if vararg generics are added to allow a generic
         | `spread` that works for any tuple and matching-arity FnOnce.
        
           | Animats wrote:
           | There's                   o.expect("Error")?.map(f)
           | 
           | if you just need to convert a Option to a value in-line and
           | None is an error.
        
           | yakubin wrote:
           | [EDIT: comment applicable only to an older version of the
           | parent comment]
           | 
           | This "foo" still suffers from the same problem as
           | "Option::map" does: it is hard-coded for functions of fixed
           | arity (2 in this case, 1 in Option::map's case).
           | 
           | Needing to write this function doesn't shorten the code. The
           | SML-inspired solution allows one to write an "Option::map"
           | which accepts functions of all possible arities (since they
           | are all 1).
           | 
           | But yes, if there are a lot of places where I want to call
           | "Option::map" for an "f" which takes 2 arguments, that could
           | work. Although, personally, I think I'd still opt for an
           | explicit lambda, in order to communicate intent more directly
           | (avoid one level of indirection).
        
             | Arnavion wrote:
             | >This "foo" still suffers from the same problem as
             | "Option::map" does: it is hard-coded for functions of fixed
             | arity (2 in this case, 1 in Option::map's case).
             | 
             | Hence the last paragraph in my comment.
        
               | yakubin wrote:
               | Sorry. I didn't see it before. You're right.
        
               | Arnavion wrote:
               | Actually, I'm sorry too. I just realized I had a
               | brainfart while writing that example and forgot to make
               | the point I wanted to make. I've fixed it now. (It was
               | supposed to work with any-arity tuples as well.)
        
         | kaszanka wrote:
         | > the "strum" crate, which has a macro to produce another
         | (plain) enum, but having the discriminant type be named _"
         | std::mem::Discriminant<T>"_
         | 
         | I don't understand, is this a copy/paste mistake?
        
           | yakubin wrote:
           | You cut out the rest of the sentence, which is needed to make
           | sense of it.
           | 
           | What I mean is, with the "strum" crate I can use a proc macro
           | to automatically generate an "EnumDiscriminant" enum for an
           | "Enum" enum, where "Enum" is defined as:                 enum
           | Enum {           A(u32),           B,       }
           | 
           | and "EnumDiscriminant" is defined as:                 enum
           | EnumDiscriminant {           A,           B,       }
           | 
           | However, the only thing linking those two is a naming
           | convention. I would like to have a universal name for such
           | discriminants.
           | 
           | The Rust standard library already has such a discriminant
           | type for any enum - mem::Discriminant<T>, where T is the enum
           | type. However, in order to get the discriminant of Enum::A(5)
           | I need to write mem::discriminant(Enum::A(5)), which
           | introduces noise (especially with bigger tuples or structs) -
           | this 5 here is irrelevant. The mem::discriminant() function
           | is useful for contexts where I have a variable of type Enum
           | and want to get its discriminant, but when I just want to
           | pick a particular discriminant, it's too noisy. I would like
           | to be able to name the discriminant directly. I.e. I would
           | like the standard library type Discriminant<T> be defined for
           | each enum the same way EnumDiscriminant above is defined for
           | Enum. So I could just write Discriminant::<Enum>::A.
        
         | nynx wrote:
         | Having enum variants be structs has been proposed, and that
         | could solve your issue if `mem::disciminant<Enum::Variant>`
         | returned what you wanted.
        
           | yakubin wrote:
           | _> Having enum variants be structs has been proposed_
           | 
           | That's great news.
           | 
           |  _> that could solve your issue if `mem::disciminant
           | <Enum::Variant>` returned what you wanted._
           | 
           | I think you misunderstood, or I explained it poorly.
           | 
           | Given an:                 enum Enum {           A(int),
           | B,       }
           | 
           | I would like mem::Discriminant<Enum> be the same type as if
           | there was such a definition in code (illegal in Rust, because
           | Rust doesn't have specializations):                 enum
           | mem::Discriminant<Enum> {           A,           B,       }
        
             | nynx wrote:
             | Ah, I did misunderstand. That's an interesting idea.
        
       | maxioatic wrote:
       | This little bit, which is not coming in this version but is
       | enabled by it:
       | 
       | > f"" as a short-hand for a format string. For example, f"hello
       | {name}" as a short-hand for the equivalent format_args!()
       | invocation.
       | 
       | I'm excited by. I like the f-string formatting in Python quite a
       | bit.
        
         | throwaway894345 wrote:
         | f-strings in Python resolve to strings, but f-strings in Rust
         | will resolve to std::fmt::Arguments IIUC. Not quite the same
         | thing.
        
           | kibwen wrote:
           | Yes, there's a lot of small details that need to be ironed
           | out before we get a clear picture of what the feature _might_
           | be (remember, there 's no guarantee!). There's a reason this
           | is simply reserving the syntax, rather than actually
           | proposing the feature. :)
        
         | steveklabnik wrote:
         | Our original formatting was inspired by C#, this is inspired by
         | Python. We stand on the shoulders of giants, as always.
        
           | maxioatic wrote:
           | If you don't mind me asking - what goes into implementing a
           | feature like this in Rust? Asking as a complete noob in
           | language design/implementation.
        
             | steveklabnik wrote:
             | In general, nindalf's reply is very on point about the
             | mechanics. To expand ever so slightly on this bit:
             | 
             | > If the folks who work in that area are supportive
             | 
             | Rust has teams that make decisions to accept designs in
             | their part of the project. They look at proposals and
             | decide to accept, reject, or postpone them. This process
             | can take a while, depending on all sorts of factors.
             | Sometimes a design may be good, but it may not be the time
             | yet, which is when postponing happens. Sometimes it's a "we
             | don't plan on doing this" and that's when things get
             | rejected. Even if a design is accepted, we don't require
             | that people proposing the design do the implementation
             | work, so if you do propose something and it does get
             | accepted, it may take a while until it actually exists.
             | Furthermore, stuff is in unstable at first, until people
             | can gain experience with the feature, so even after an RFC
             | is accepted, it takes some time until it's "stabilized," at
             | which point it's part of the language proper.
             | 
             | Happy to answer any other specific questions!
        
               | maxioatic wrote:
               | Thanks for the detailed response! I checked out the RFC
               | repo posted by nindalf, and I was wondering if you could
               | explain (very briefly) the issue tagging scheme? Do the
               | "A-" tagged issues mean they're active?
               | 
               | Edit: For more context, the README says:
               | 
               | > If you are interested in working on the implementation
               | for an "active" RFC, but cannot determine if someone else
               | is already working on it, feel free to ask (e.g. by
               | leaving a comment on the associated issue).
               | 
               | I went and checked out the issues and couldn't tell which
               | ones were active. (I also don't plan on implementing an
               | RFC, so no worries if this is something that should be
               | apparent to someone more involved.)
        
               | steveklabnik wrote:
               | "A" is short for "Area", which is something we inherited
               | from Mozilla, IIRC.
               | 
               | I don't think there's a good way to filter on "active",
               | it's mostly like, if you go to the issue and it's open,
               | then it's 'active.' It's a bit odd, because in some
               | sense, for the RFC repo, once the RFC is merged it's
               | "done", and then rest of it is implementation and
               | therefore tracked on the main Rust repo. The "C-tracking-
               | issue" issues are ones that are open and tracking an RFC
               | that's in implementation, https://github.com/rust-
               | lang/rust/issues?q=is%3Aissue+is%3Ao...
               | 
               | ("C" is short for "Category". These prefixes are... well
               | the original ones made sense but I think there was some
               | retconning going on at some point, haha)
        
             | nindalf wrote:
             | You want to take a look at the RFC process. You could start
             | with the feature we're discussing right now - Implicit
             | arguments in format strings (https://rust-
             | lang.github.io/rfcs/2795-format-args-implicit-i...). Take a
             | look, it's very readable.
             | 
             | There's discussion in the corresponding Github issue of the
             | RFCs repo (https://github.com/rust-lang/rfcs). If the folks
             | who work in that area are supportive, the RFC will be
             | merged. After that someone needs to implement the feature.
             | 
             | Take a look at the Readme in the RFCs repo for more
             | details.
        
               | maxioatic wrote:
               | Thanks for the info, appreciate it!
        
               | k__ wrote:
               | I'm pretty excited about more implicit stuff coming to
               | Rust.
               | 
               | The array/iterator thing always baffled me when I first
               | started learning Rust.
               | 
               | All these tiny DX improvements make the language more
               | accessible and in turn a pleasure to work with!
        
           | Macha wrote:
           | Didn't realise the C# intermediate step was there, always
           | assumed it was a direct Python influence.
        
             | steveklabnik wrote:
             | Oh you know, I was confused. It was attributes from C#, not
             | format strings. https://doc.rust-
             | lang.org/stable/reference/attributes.html#a...
             | 
             | > Attributes are modeled on Attributes in ECMA-335, with
             | the syntax coming from ECMA-334 (C#).
        
               | kibwen wrote:
               | I believe Rust took both attributes and format strings
               | from C#. It looks like Python's modern format string
               | syntax dates to 2006, and I think(?) that C#'s format
               | string syntax has been present since its first release in
               | 2002. The PEP adding the modern string formatting syntax
               | links to .NET documentation, although the link is now
               | dead:
               | https://www.python.org/dev/peps/pep-3101/#references
        
               | UtherII wrote:
               | You can even go back earlier : MessageFormat is available
               | in Java since 1997.
        
         | dguaraglia wrote:
         | I remember hating the idea when the PEP was at first accepted.
         | I hated the thought of refactoring tools breaking, typos
         | leading to empty replacements, etc.
         | 
         | In reality, f-strings work really well and are super elegant.
         | I'm a convert now.
        
         | ibraheemdev wrote:
         | There is also the fstrings crate that emulates this with proc
         | macros: https://crates.io/crates/fstrings
        
       | dmkolobov wrote:
       | Lack of IntoIterator for arrays and disjoint capture are some of
       | the biggest pain-points in writing Rust code for me! Very excited
       | to see these changes on the map.
        
       | qzw wrote:
       | > Instead, we decided to add the [IntoIterator] trait
       | implementation in all editions (starting in Rust 1.53.0), but add
       | a small hack to avoid breakage until Rust 2021.
       | 
       | Eh, why not. We nerds are used to retconning in all the sci-fi
       | universes anyway.
        
       | RhodoGSA wrote:
       | Decided to start learning rust last month. I have very little
       | previous programming experience and the journey has been a bit
       | brutal so far so i wanted to share my "Learning journey".
       | 
       | "The book" although a great book, is fairly dense with no
       | exercises. Rustlings[1] help me apply alot of my learning and i
       | supplemented the book with Tensors programming tutorials on
       | youtube [2]. Recently i started developing a game in Bevy[3] and
       | although its a bit over my head at this point i've managed to get
       | a character moving and be able to kill a monster and receive a
       | drop. My motivations for learning rust was to be familiar with
       | substrate [4] as i'd like to switch careers into programming.
       | Would also love any recommendations/tips & tricks by any
       | experience rustaceans!
       | 
       | [1]https://github.com/rust-lang/rustlings [2]https://www.youtube.
       | com/watch?v=EYqceb2AnkU&list=PLJbE2Yu2zu...
       | [3]https://bevyengine.org/learn/book/getting-started/
       | [4]https://www.substrate.io/
        
       ___________________________________________________________________
       (page generated 2021-05-11 23:01 UTC)