Wedson Almeida Filho is a Microsoft engineer who has been prolific in his contributions to the Rust for the Linux kernel code over the past several years. Wedson has worked on many Rust Linux kernel features and even did a experimental EXT2 file-system driver port to Rust. But he’s had enough and is now stepping away from the Rust for Linux efforts.

From Wedon’s post on the kernel mailing list:

I am retiring from the project. After almost 4 years, I find myself lacking the energy and enthusiasm I once had to respond to some of the nontechnical nonsense, so it’s best to leave it up to those who still have it in them.

I truly believe the future of kernels is with memory-safe languages. I am no visionary but if Linux doesn’t internalize this, I’m afraid some other kernel will do to it what it did to Unix.

Lastly, I’ll leave a small, 3min 30s, sample for context here: https://youtu.be/WiPp9YEBV0Q?t=1529 – and to reiterate, no one is trying force anyone else to learn Rust nor prevent refactorings of C code."

    • fluxx@lemmy.world
      link
      fedilink
      arrow-up
      20
      ·
      3 months ago

      Well, I’ve been a C/C++ dev for half of my career, I didn’t find Rust syntax ugly. Some things are better than others, but not a major departure from C/C++. ObjC is where ugly is at. And I even think swift is more ugly. In fact, I can’t find too many that are as close to C/C++ as Rust. As for logic… Well, I want to say you’ll get used to it, but for some things, it’s not true. Rust is a struggle. Whether it’s worth it, is your choice. I personally would take it over C++ any day.

    • OrekiWoof@lemmy.ml
      link
      fedilink
      arrow-up
      15
      arrow-down
      8
      ·
      3 months ago

      I just don’t understand this. You get used to the syntax and borrow checker in a day or two. It’s a non-issue.

      • thevoidzero@lemmy.world
        link
        fedilink
        arrow-up
        12
        ·
        3 months ago

        I wouldn’t say that. For primitives yeah, day or two. But if you want to build a proper program, it’ll take time to get used to it. For my first few projects I just used clone everywhere. Passing by reference and managing lifetimes, specially when writing libraries is something that takes time to get used to. I still don’t feel confident.

        Besides that I do like Rust though. Sometimes I feel like “just let me do that, C let’s me”, but I know it’s just adding safety where C wouldn’t care.

      • pivot_root@lemmy.world
        link
        fedilink
        arrow-up
        9
        arrow-down
        1
        ·
        3 months ago

        Unless you’re a functional programming purist or coming from a systems programming background, it takes a lot longer than a few days to get used to the borrow checker. If you’re coming as someone who most often uses garbage-collected languages, it’s even worse.

        The problem isn’t so much understanding what the compiler is bitching about, as it is understanding why the paradigm you used isn’t safe and learning how to structure your code differently. That part takes the longest and only really starts to become easier when you learn to stop fighting the language.

        • OrekiWoof@lemmy.ml
          link
          fedilink
          arrow-up
          4
          ·
          3 months ago

          I see that my previous comment is not the common reality apparently.

          I’m mainly a C# + js dev of a few years, and I would love to see what precisely other people here are having problems with, because I’ve had a completely different experience to most of the people replying.

      • Preston Maness ☭@lemmygrad.ml
        link
        fedilink
        English
        arrow-up
        8
        ·
        edit-2
        3 months ago

        You get used to the syntax and borrow checker in a day or two.

        As someone who spent a couple months learning rust, this was half true for me. The syntax? Yeah. No problem. The borrow-checker (and Rust’s concept of ownership and lifetimes in general)? Absolutely not. That was entirely new territory for me.

          • Preston Maness ☭@lemmygrad.ml
            link
            fedilink
            English
            arrow-up
            1
            ·
            edit-2
            3 months ago

            I’ll try :) Looks like I still have my code from when I was grinding through The Book, and there’s a couple spots that might be illuminating from a pedagogical standpoint. That being said, I’m sure my thought process, and “what was active code and what was commented out and when,” will probably be hard to follow.

            My first confusion was in deref coercion auto dereferencing (edit: see? it’s still probably not 100% in my head :P), and my confusion pretty much matched this StackOverflow entry:

            https://stackoverflow.com/questions/28519997/what-are-rusts-exact-auto-dereferencing-rules

            It took me until Chapter 15 of The Book (on Boxes) to really get a feel for what was happening. My work and comments for Chapter 15:

            use crate::List::{Cons, Nil};
            use std::ops::Deref;
            
            enum List {
                Cons(i32, Box<List>),
                Nil,
            }
            
            struct MyBox<T>(T);
            
            impl<T> Deref for MyBox<T> {
                type Target = T;
                fn deref(&self) -> &Self::Target {
                    &self.0
                }
            }
            
            impl<T> MyBox<T> {
                fn new(x: T) -> MyBox<T> {
                    MyBox(x)
                }
            }
            
            #[derive(Debug)]
            struct CustomSmartPointer {
                data: String,
            }
            
            impl Drop for CustomSmartPointer {
                fn drop(&mut self) {
                    println!("Dropping CustomSmartPointer with data `{}`!", self.data);
                }
            }
            
            fn main() {
                let b = Box::new(5);
                println!("b = {}", b);
            
                let _list = Cons(1, Box::new(Cons(2, Box::new(Cons(3,Box::new(Nil))))));
            
                let x = 5;
                let y = MyBox::new(x);
            
                assert_eq!(5,x);
                assert_eq!(5, *y);
            
                let m = MyBox::new(String::from("Rust"));
                hello(&m);
                hello(m.deref());
                hello(m.deref().deref());
                hello(&(*m)[..]);
                hello(&(m.deref())[..]);
                hello(&(*(m.deref()))[..]);
                hello(&(*(m.deref())));
                hello((*(m.deref())).deref());
            
                // so many equivalent ways. I think I'm understanding what happens
                // at various stages though, and why deref coercion was added to
                // the language. Would cut down on arguing over which of these myriad
                // cases is "idomatic." Instead, let the compiler figure out if there's
                // a path to the desired end state (&str).
            
                // drop stuff below ...
                let _c = CustomSmartPointer {
                    data: String::from("my stuff"),
                };
                let _d = CustomSmartPointer {
                    data: String::from("other stuff"),
                };
            
                println!("CustomSmartPointers created.");
                drop(_c);
                println!("CustomSmartPointer dropped before the end of main.");
            
                // this should fail.
                //println!("{:?}", _c);
                // yep, it does.
            
            }
            
            fn hello(name: &str) {
                println!("Hello, {name}!");
            }
            

            Another thing that ended up biting me in the ass was Non-Lexical Lifetimes (NLLs). My code from Chapter 8 (on HashMaps):

            use std::collections::HashMap;
            
            fn print_type_of<T>(_: &T) {
                println!("{}", std::any::type_name::<T>())
            }
            
            fn main() {
                let mut scores = HashMap::new();
                scores.insert(String::from("Red"), 10);
                scores.insert(String::from("Blue"), 20);
            
                let score1 = scores.get(&String::from("Blue")).unwrap_or(&0);
                println!("score for blue is {score1}");
                print_type_of(&score1); //&i32
                let score2 = scores.get(&String::from("Blue")).copied().unwrap_or(0);
                println!("score for blue is {score2}");
                print_type_of(&score2); //i32
            
                // hmmm... I'm thinking score1 is a "borrow" of memory "owned" by the
                // hashmap. What if we modify the blue teams score now? My gut tells
                // me the compiler would complain, since `score1` is no longer what
                // we thought it was. But would touching the score of Red in the hash
                // map still be valid? Let's find out.
            
                // Yep! The below two lines barf!
                //scores.insert(String::from("Blue"),15);
                //println!("score for blue is {score1}");
            
                // But can we fiddle with red independently?
                // Nope. Not valid. So... the ownership must be on the HashMap as a whole,
                // not pieces of its memory. I wonder if there's a way to make ownership
                // more piecemeal than that.
                //scores.insert(String::from("Red"),25);
                //println!("score for blue is {score1}");
            
                // And what if we pass in references/borrows for the value?
                let mut refscores = HashMap::new();
                let mut red_score:u32 = 11;
                let mut blue_score:u32 = 21;
                let default:u32 = 0;
                refscores.insert(String::from("red"),&red_score);
                refscores.insert(String::from("blue"),&blue_score);
            
                let refscore1 = refscores.get(&String::from("red")).copied().unwrap_or(&default);
                println!("refscore1 is {refscore1}");
            
                // and then update the underlying value?
                // Yep. This barfs, as expected. Can't mutate red_score because it's
                // borrowed inside the HashMap.
                //red_score = 12;
                //println!("refscore1 is {refscore1}");
            
                // what if we have mutable refs/borrows though? is that allowed?
                let mut mutrefscores = HashMap::new();
                let mut yellow_score:u32 = 12;
                let mut green_score:u32 = 22;
                let mut default2:u32 = 0;
                mutrefscores.insert(String::from("yellow"),&mut yellow_score);
                mutrefscores.insert(String::from("green"),&mut green_score);
                //println!("{:?}", mutrefscores);
            
                let mutrefscore1 = mutrefscores.get(&String::from("yellow")).unwrap();//.unwrap_or(&&default2);
                //println!("{:?}",mutrefscore1);
                
                println!("mutrefscore1 is {mutrefscore1}");
            
                // so it's allowed. But do we have the same "can't mutate in two places"
                // rule? I think so. Let's find out.
            
                // yep. same failure as before. makes sense.
                //yellow_score = 13;
                //println!("mutrefscore1 is {mutrefscore1}");
            
                // updating entries...
                let mut update = HashMap::new();
                update.insert(String::from("blue"),10);
                //let redscore = update.entry(String::from("red")).or_insert(50);
                update.entry(String::from("red")).or_insert(50);
                //let bluescore = update.entry(String::from("blue")).or_insert(12);
                update.entry(String::from("blue")).or_insert(12);
            
            
                //println!("redscore is {redscore}");
                //println!("bluescore is {bluescore}");
                println!("{:?}",update);
            
                // hmmm.... so we can iterate one by one and do the redscore/bluescore
                // dance, but not in the same scope I guess.
                let mut updatesingle = HashMap::new();
                updatesingle.insert(String::from("blue"),10);
                for i in "blue red".split_whitespace() {
                    let score = updatesingle.entry(String::from(i)).or_insert(99);
                    println!("score is {score}");
                }
            
                // update based on contents
                let lolwut = "hello world wonderful world";
                let mut lolmap = HashMap::new();
                for word in lolwut.split_whitespace() {
                    let entry = lolmap.entry(word).or_insert(0);
                    *entry += 1;
                }
            
                println!("{:?}",lolmap);
            
                // it seems like you can only borrow the HashMap as a whole.
                // let's try updating entries outside the context of a forloop.
            
                let mut test = HashMap::new();
                test.insert(String::from("hello"),0);
                test.insert(String::from("world"),0);
                let hello = test.entry(String::from("hello")).or_insert(0);
                *hello += 1;
                let world = test.entry(String::from("world")).or_insert(0);
                *world += 1;
            
                println!("{:?}",test);
            
                // huh? Why does this work? I'm borrowing two sections of the hashmap like before in the update
                // section.
                
                // what if i print the actual hello or world...
                // nope. barfs still.
                //println!("hello is {hello}");
            
                // I *think* what is happening here has to do with lifetimes. E.g.,
                // when I introduce the println macro for hello variable, the lifetime
                // gets extended and "crosses over" the second borrow, violating the
                // borrow checker rules. But, if there is no println macro for the hello
                // variable, then the lifetime for each test.entry is just the line it
                // happens on.
                //
                // Yeah. Looks like it has to do with Non-Lexical Lifetimes (NLLs), a
                // feature since 2018. I've been thinking of lifetimes as lexical this
                // whole time. And before 2018, that was correct. Now though, the compiler
                // is "smarter."
                //
                // https://stackoverflow.com/questions/52909623/rust-multiple-mutable-borrowing
                //
                //   https://stackoverflow.com/questions/50251487/what-are-non-lexical-lifetimes
                //let 
            }
            
            • OrekiWoof@lemmy.ml
              link
              fedilink
              arrow-up
              2
              ·
              2 months ago

              That’s insightful, thank you. It wasn’t hard to follow, I did have these exact same “adventures” but I guess I forgot about them after I figured out the ways to do things.

              Personally these kinds of things are exciting for me, trying to understand the constraints etc, so maybe that’s also why I don’t remember struggling with learning Rust, since it wasn’t painful for me 😅 If someone has to learn by being forced to and not out of their own will, it’s probably a lot harder

      • Metype @lemmy.world
        link
        fedilink
        English
        arrow-up
        4
        ·
        3 months ago

        I tried for about a week: reading documentation, viewing and modifying example programs, using a Rust IDE with warnings for all my silly mistakes, the works. I couldn’t manage to wrap my head around it. It’s so different from what I’m used to. If I could dedicate like a month to learn it I would, but I don’t have the time :/

    • ReakDuck@lemmy.ml
      link
      fedilink
      arrow-up
      5
      ·
      3 months ago

      At rhe beginning, I did hate it. Now I slowly embrace it as it seems like a feature to be mkre verbose.

      But maybe it will never change and I will just gaslight myself liking it. Whatever… you cant take my fun away learning rust for half a year

        • ReakDuck@lemmy.ml
          link
          fedilink
          arrow-up
          1
          ·
          2 months ago

          I hate how I can’t do everything I imagine in rust.

          But researching about why something isn’t possible, makes me realize that the code should never be wroten like the way I did… so I can’t blame rust for dissallowing me this.

    • Wooki@lemmy.world
      link
      fedilink
      arrow-up
      3
      arrow-down
      1
      ·
      edit-2
      2 months ago

      You’ve been blue pilled by null. Once over the hurdle, it’s very eloquent.

      Null is ugly. Tony Hoare apology for inventing it should be enough reason to learn to do better.