• mholiv@lemmy.world
    link
    fedilink
    arrow-up
    33
    arrow-down
    2
    ·
    1 year ago

    Skill Issue.

    For reals though adopting a functional style of programming makes rust extremely pleasant . It’s only when people program in object oriented styles that this gets annoying.

    No loops, and no state change make rust devs happy devs.

    • AnarchoEngineer@lemmy.dbzer0.com
      link
      fedilink
      arrow-up
      4
      ·
      1 year ago

      I just started learning rust like two days ago and I haven’t had too many issues with OOP so far… is it going to get considerably worse as the complexity of my projects increases?

      • Ephera@lemmy.ml
        link
        fedilink
        English
        arrow-up
        9
        arrow-down
        1
        ·
        1 year ago

        The thing with OOP, particularly how it’s used in GCed languages, is that it’s all about handing references out to wherever and then dealing with the complexity of not knowing who has access to your fields via getters & setters, or by cloning memory whenever it’s modified in asynchronous code.

        Rust has quite the opposite mindset. It’s all about tracking where references go. It pushes your code to be very tree-shaped, i.e. references typically¹ only exist between a function and the functions it calls underneath. This is what allows asynchronous code to be safe in Rust, and I would also argue that the tree shape makes code easier to understand, too.

        But yeah, some of the patterns you might know from OOP will not work in Rust for that reason. You will likely need to get into a different mindset over time.

        Also just in case: We are talking OOP in the sense of the paradigm, i.e. object-oriented.
        Just using objects, i.e. data with associated functions/methods, that works completely normal in Rust.

        ¹) If you genuinely need references that reach outside the tree shape, which is mostly going to be the case, if you work with multiple threads, then you can do so by wrapping your data structures in Arc<Mutex<_>> or similar. But yeah, when learning, you should try to solve your problems without these. Most programs don’t need them.

        • dejected_warp_core@lemmy.world
          link
          fedilink
          arrow-up
          1
          ·
          1 year ago

          It pushes your code to be very tree-shaped

          This is basically where my learning took me. I had to develop this notion that there was a preferred directionality to ownership and data flow, like “grain” in a piece of wood. Everything is easier if you go with the grain. “Tree-shaped” works too, since it basically is the call graph of a (single threaded) program.

          The point where I realized all this was when I tried to do a very Python/JS-brained thing: return a closure from a function. The moment you try to “curry” values into the closure, you have to “move” them to solve for ownership, lest you bring timelines into the picture. Which isn’t always what you want in a generic and reusable function. And sure enough, the standard lib and other popular libraries want you to pass a closure to functions instead.

          • Ephera@lemmy.ml
            link
            fedilink
            English
            arrow-up
            1
            ·
            1 year ago

            Hmm, not sure, if I’ve heard of it. I’m guessing, we’re not talking about simply drawing a UML class diagram…? Is it for figuring out which object will have to clean up which other objects, in non-GCed languages?

              • Ephera@lemmy.ml
                link
                fedilink
                English
                arrow-up
                1
                ·
                1 year ago

                Ah, interesting. I went from garbage-collected languages where thinking about ownership might be useful for keeping complexity low and occasionally comes up when you manage lists of objects, but ultimately isn’t needed, to Rust where well-defined ownership is enforced by the language.

                So, I wasn’t aware that ownership is even as concrete of a thing in other languages…

                • Oh you need this in garbage collected languages too, once you run into memory use issues. GC languages are notorious for being wasteful with memory, even when working correctly.

      • qaz@lemmy.world
        link
        fedilink
        English
        arrow-up
        6
        ·
        1 year ago

        It will become more complex when you start needing circular references in your datastructures.

      • mholiv@lemmy.world
        link
        fedilink
        arrow-up
        4
        arrow-down
        1
        ·
        1 year ago

        You’ll be fine. You will learn the lifetime stuff and all will work out. It’s not that bad to be honest.

  • diffusive@lemmy.world
    link
    fedilink
    arrow-up
    27
    ·
    1 year ago

    Call me a weirdo but the more errors a compilers give me the happier (albeit a bit frustrated) I am. That stuff generally surfaces in a way or another… and I prefer at compile time 🙂

    That said I haven’t spent quality time with Rust yet… so not sure if there are a lot of nitpicks (ala go) or these are valgrind-level of “holy s*** I am so grateful to this tool” 😃

    • itslilith@lemmy.blahaj.zone
      link
      fedilink
      arrow-up
      6
      ·
      1 year ago

      The borrow checker makes things a bit more complicated to get running, definitely takes some getting used to when you come from a non-memory safe language. But the compiler is really helpful throughout almost all mistakes, often directly providing an explanation and a suggested fix. One of my favorites programming experiences so far

      • qaz@lemmy.world
        link
        fedilink
        English
        arrow-up
        6
        ·
        edit-2
        1 year ago

        …definitely takes some getting used to when you come from a non-memory safe language…

        I actually think it’s more like the opposite. The compiler takes the normal rules you apply to avoid issues with a non-memory safe language like C/C++ and enforces them explicitly where memory safe languages don’t have those rules at all. I think lifetimes are much more confusing if you’ve never dealt with a user after free and usually let GC deal with it.

        Also yes the compiler warnings and errors are amazing, the difference between rustc and gcc is night and day.

  • Lovable Sidekick@lemmy.world
    link
    fedilink
    English
    arrow-up
    15
    ·
    edit-2
    1 year ago

    The old school method of learning a programming language, database, framework or whatever was to read books and take classes, do a series of exercises that teach you how to use the features, and the errors you get if you don’t do it right. Then you write code that way for like 10-15 years.

    The Information Age method is to find some sample code, copypaste into an editor and hit Compile, then paste compile errors into google and fix them until there are no more. Then hit Run and copypaste/fix runtime errors until there are no more runtime errors. Old-schoolers used to call this hacking, but now it’s called not having time to deeply learn the hot new thing because before you do you’ll have to start over with the next hot new thing.

    • Tekhne@sh.itjust.works
      link
      fedilink
      arrow-up
      2
      ·
      1 year ago

      The last language I learned was Rust, I did a mix of the two. I read through the canonical Rust book and then got to coding because I learn more deeply when I can apply what I’ve learned. It’s still a tricky language to keep a conceptual model of in your head though.

  • Kamikaze Rusher@lemmy.world
    link
    fedilink
    arrow-up
    14
    ·
    1 year ago

    This is my experience every time I return to learning rust. I’m guessing that if I used it more often than once a quarter with hobby projects I’d stop falling into the same traps.

    • Ephera@lemmy.ml
      link
      fedilink
      English
      arrow-up
      7
      ·
      1 year ago

      Yeah, these become a lot less relevant with routine.

      • Avoiding the main-thread panicking is mostly just a matter of not using .unwrap() and .expect().

      • String vs. &str can mostly be solved by generally using owned datatypes (String) for storing in structs and using references (&str) for passing into function parameters. It does still happen that you forget the & at times, but that’s then trivial to solve (by just adding the &).

      • “temporary value dropped while borrowed” can generally be avoided by not passing references outside of your scope/function. You want to pass the owned value outside. Clone, if you have to.

      • “missing lifetime specifier” is also largely solved by not storing references in structs.

      • Kamikaze Rusher@lemmy.world
        link
        fedilink
        arrow-up
        5
        ·
        1 year ago

        The last two points are the kind of design advice I need to see. I’m probably so used to the C/C++ concept of passing by reference to prevent copies of complex data being generated that I forget how Rust’s definition of a reference is different.

  • dejected_warp_core@lemmy.world
    link
    fedilink
    arrow-up
    1
    ·
    edit-2
    1 year ago

    This was my experience too, until I learned a few things.

    • If you’re coming from another programming language, the equivalent capabilities you’re probably used to are Box, dyn, and Rc.
    • Dynamic dispatch (dyn) isn’t really necessary a lot of the time. Identify where you absolutely need it and solve everything else through other means.
    • You wind up with lifetime specifier problems if you try to do a lot with references (&). Instead, try to re-think your structs and functions using composition and clone/copy instead. It’s less efficient, but it’s easier to optimize a running program, too.
    • Rust enum, match, if let, and ? are weird, but are where you get the most leverage in the language. Try to master them.
    • derive[...] is a first-class feature with a lot of standard lib support. Always use this to make your custom types mesh with the standard lib more seamlessly.
    • If you are experienced with the “Design Patterns” book, you absolutely need this: https://rust-unofficial.github.io/patterns/intro.html
    • Macros are an advanced feature, but help get you around limitations in generics and the type system in general. it really is worth knowing, and like the preprocessor in C/C++, isn’t avoidable at the intermediate level.
    • The compiler digs deep into your code to figure out types where they’re not explicitly declared. I’ve seen it reach into the return type, call-spec, and function calls within that function, to figure out types for things. This is very hard to observe without an IDE that’s checking syntax on the fly. Lean into both of those for more readable and maintainable code.
    • if and match are expressions, not statements! You can use either block to evaluate to a single value, useful in composite expressions like let. Example; let x=if y>20 { y } else { 0 }; Or use them to return values from functions (w/o need of a return statement).