Announcement

Collapse
No announcement yet.

Fedora 38 Looks To Shift RPM To Sequoia, A Rust-Based OpenPGP Parser

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • #21
    Originally posted by lowflyer View Post
    There is that section in the rust documentation about "validating references with lifetimes". Every C++ compiler would vomit back at you when you try to write something similar. In C++ it's impossible to create a reference before you "know" the referred object. This example is outright crazy to a C++ developer. To me it looks like the classic straw-man argument: You let rust do something that is not even possible in C++, and then claim superiority because rust doesn't compile. Apparently rust requires a "borrow checker" to prevent something really stupid.
    Which example are you referring to? Note that the blocks with a crab and a question mark DON'T compile. The surrounding text explains what's wrong with it.

    Comment


    • #22
      Originally posted by NobodyXu View Post

      There's also mutability rule that prevents you from modifying one variable concurrently from multiple threads without synchronization.

      For example, you cannot mutably borrow the same variable twice (create multiple alias), send them to two threads and have them modify it concurrently.
      You would have to wrap that variable in Mutex/RwLock, then send the immutable reference, or use atomic.

      This also enables an additional optimization called "no alias".
      The rust compiler can guarantee that if you have a mutable reference, then it must be the only one that can access it until the reference is dropped.

      It seems that C can emulate that using "restrict" and C++ can use "restrict" as a GNU extension, but C/C++ compiler does not seem to use them as clang implementation for this optimization turns out to be extremely buggy.

      Rustc has attempted to switch on "noalias" optimization in LLVM for several times and it all got disabled soon after rolling out in nightly channel because some miscompilation in LLVM.
      Look at it this way: I talk about "lifetime" and "reference counting" and you reply with "multithreading", "synchronization", "mutex" and "atomic". This smells an awful lot like another strawman argument.

      In C++, multithreading is not a component of the compiler. It is implemented in a library. While in rust, it seems to be, an inherent component within the compiler.

      You praise the "no alias" as an additional optimization. While I doubt that this will always make the code run faster, I wonder what hoops you need to go through with rust if you *just need to do that* (access a mutable reference from multiple threads). In rust, "references" seem to be literally *everywhere*. At least this is what I glean from multiple rust examples I've been shown here and in the past. Can you actually do something sensible in rust *without using references*? Or is this just used as another (strawman-) argument against other languages?

      Originally posted by NobodyXu View Post
      Well, that's the very simple example.
      In real life code get much complicated than that.
      If the code to show the superiority of rust over *any* other language needs much more complication, then I fail to see the point of it.

      Originally posted by NobodyXu View Post
      One simple example that clang/clang++ might fail is holding a reference to a element in std::vector, resize std::vector and then access it again.
      If you have a function "int& f(std::vector &v);` that takes vector and return a reference, then later resize the vector, I doubt C++ compiler can catch it.
      Cppcheck catches it:
      image.png
      (similar code on godbolt) Perhaps you argue "but rust has it built into the compiler"​. Well, cppcheck catches it *before* the compilation.

      Originally posted by NobodyXu View Post
      A more complex example involves async function.
      async in Rust is where multiple futures can be executed concurrently but not necessary in parallel.

      In futures-util, there's a function called "try_join", which let's you execute two futures concurrently in one thread without the overhead of thread synchronization.
      This also means that you can borrow local variables without using "Arc" (which is reference counting).

      This is where the real fun starts.
      Without borrow checking, it's probably very easy to get that wrong.

      Another example would be scope spawning https://doc.rust-lang.org/std/thread/fn.scope.html

      Using std::thread::scope, you can spawn multiple threads that borrows local variable while still guarantee there's no use-after-free.
      You can wait on the thread handle and join them or get the thread id or do whatever with them, but you cannot leak them outside that function std::thread::scope.
      That guarantees that before std::thread::scope returns, all thread spawned in the function, which could use local variables, are dropped and implicitly joined as RAII.

      It also uses subtyping to ensure that the thread created would have a lifetime smaller than the parameter "s" given to the callback:L

      Code:
      pub fn scope<'env, F, T>(f: F) -> Twhere F: for<'scope> [URL="https://doc.rust-lang.org/std/ops/trait.FnOnce.html"]FnOnce[/URL](&'scope [URL="https://doc.rust-lang.org/std/thread/struct.Scope.html"]Scope[/URL]<'scope, 'env>) -> T,
      
      // Here is where subtyping occurs, it guarantees that the 'env must be a subtype of 'scope, meaning it must outlive 'scope,
      // which is the same lifetime of Scope<'scope, 'env>.
      //
      // It ensures that this object cannot escape the callback `f` in function scope.
      // |
      pub struct Scope<'scope, 'env: 'scope> { /* private fields */ }
      
      impl<'scope, 'env> Scope<'scope, 'env> {
      // Infer lifetime 'scope to be same as lifetime of self (Scope<'scope, 'env>)
      fn spawn<F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>​;
      }
      Again, many references to multithreading, which is again sort-of evading the subject. If the strong point of rust is multithreading, you should not compare it with plain C++ object lifetimes. Despite that, I think there is nothing in what you explain above that cannot be done in C++ equally safe. I think that most of what you praise as "unique rust feature" is present in C++ and other languages already. It would be helpful if you could provide links to examples in godbolt-rust so we can really compare them. I would like to see examples for the concurrent execution of futures, the two futures in one thread with "borrowing" of variables and a thread::scope example. (I promise to put counter-examples in C++ for your rust-examples)

      Originally posted by NobodyXu View Post
      That literally would not work if lifetime works like reference counting.
      I think I did already tell you that these are not the same ...

      Originally posted by NobodyXu View Post
      C++ does not have lifetime.
      On the contrary, rust embeds lifetime in the type.
      You get that wrong. C++ indeed has lifetime. Obviously we disagree. C++ example:
      Code:
      auto main() -> int
      {
          int r;    // lifetime of "r" starts *and* type is defined
                      // impossible to define a reference of non-existing variable
          {
              int x{5};
              r = x;  // impossible to redefine "r" as reference to "x"
          }
      }        // lifetime of "r" ends​
      In rust on the other hand, lifetime is separate from the type. (at least that's how I see it)
      Code:
      fn main() {
          let r;            // lifetime of "r" starts, but there's no type yet
          {
              let x = ".53";
              r = x;    // type of "r" is "inferred"
          }
          println!("r: {}", r);
      }                     // lifetime of "r" ends



      Originally posted by NobodyXu View Post
      For example:

      Code:
      struct S<T>(T);
      In the struct declaration above, the lifetime of T is also embedded in S itself.

      So if you create

      Code:
      let i = 1;
      let s = S(&i);
      Suppose that the variable "s" has a lifetime 's and variable i has a lifetime of 'i, then `'i: 's`, meaning that 'i must outlive 's
      This is called subtyping where 'i is a subtype of 's.
      This doc explains lifetime subtyping and variance rules https://doc.rust-lang.org/nomicon/subtyping.html
      If I get that correctly, then rust needs the borrow checker to catch all the corner cases when it silently extends the lifetime of some objects to make the handling of references work. To me, it looks like rust somehow reverses the role of "reference" and "value". Or it "elevates" references to be a type of their own. It is an entirely different concept and the reason we don't understand each other when we use the words "reference" and "lifetime". Whether it is better or worse than e.g. C++ needs to be shown.

      When rustafarians (not you) say things like "the rust borrow-checker prevents all pitfalls that are in C++" I am reminded of cargo cults.
      Last edited by lowflyer; 06 December 2022, 07:49 AM.

      Comment


      • #23
        Originally posted by lowflyer View Post
        Well, first of all, it worked yay! Peeing on the rust-anthill stirred up the rustafarians. I rarely had so much "feedback" on a single post of mine...


        Are you aware that you directly contradict your fellow rustafarian NobodyXu that says "All the references are checked at compile-time"
        I don't speak for NobodyXu and don't know who he or she is. At any rate, saying that the Rust compiler does some sort of reference counting is factually incorrect.

        Originally posted by lowflyer View Post
        You also contradict your own statement when you declare "RAII enforced by a linear type system of sorts that doesn't have any counterpart in C++ in any shape or form." and further down say "RAII is obviously from C++"

        Well, I was kind of expecting that you could read. I said that RAII is from C++ but in Rust, it is modeled by a linear type system which isn't the case in C++.

        Originally posted by lowflyer View Post
        Apart from that, what you're saying here sounds like "high-level-management-talk". You know, of somebody that doesn't have a clue. I'd rather like to see you explaining how rust-RAII is different from C++-RAII *in detail*.
        In detail it's exactly what I just said: linear type system.

        Originally posted by lowflyer View Post

        What I don't like is the cult like behavior of the rustafarians (<-- derogative term for unreasonable rust fanboys). They sprinkle their sentences with buzzwords like "game changer", "coherent", "orthogonal", "robust", "linear", "integrated" and so on. Who do you guys want to impress? These words carry very little meaning if used without a significant amount of context. Are you even able to explain in detail what you mean with e.g. "coherent" and in which way it is different from other languages? We are mostly programmers here and we would understand it if you would just explain it. What do you fear?

        Linear is not a buzzword. In computer science, linear type system has an exact, narrow definition. Look it up.
        Same for orthogonal: in C++, you basically always need to select a subset of the language that you can use at any given time because the various features don't integrate well together. For example you can't throw exceptions from threads or coroutines, you can't use separate compilation units if you use templates, etc. In Rust, the language designers spend considerable effort making sure that each new feature fits well into the language as a whole. There are some exceptions to this, but those are few and far in between. In practice you can write idiomatic Rust code without worrying that using feature A will break feature B.

        As for explaining it more, that's obviously futile as you admitted yourself in your 1st paragraph that you were here to troll, not to ask genuine questions and seek actual answers. If you were interested, there is a wealth of peer-reviewed research papers, language documentation and tutorials on the subject.

        Originally posted by lowflyer View Post
        "schoolyard-level C++ fanboys" ??? Go, find me one!
        According to all these rustafarians, C++ is so extremely hard to learn that you will need at least 20 centimeters of beard before you can claim anything C++.
        The problem of C++ is not that it's hard to learn, the problem is that code written in it is unprovable and, simultaneously, extremely error-prone.

        Originally posted by lowflyer View Post
        I see rustafarians outdo each other with claims about "type safety" and other buzzwords. At each brief try with that language I come across the "unsafe" keyword. I can't help it, it feels like a joke. Religious sticking to the buzzwords while ignoring the failures is the definition of a "cargo cult"
        So firstly "unsafe" doesn't mean that Rust rules don't apply. It doesn't override the type system nor the borrow checker. All it does is that it allows you to initialise pointers to any address, which is needed in particular in drivers. It is also required for FFI calls. Since many crates on crates.io are bindings of some sort, they are obviously full of "unsafe" for that reason. In normal code "unsafe" is basically never used.

        Originally posted by lowflyer View Post
        There are other languages out there that have tried and accomplished much but still failed in the end. (remember Ada) Rust may have potential. But it does not yet have merits. Cargo cults rarely help achieving that.
        Ada failed because until gnat came along (which was already too late), all compilers were proprietary, expensive and required considerable hardware resources. You couldn't just get Ada for free and start playing around. For that reason few people learnt it and no-one except the DoD wanted to use it for development since the language alone implied considerable financial overhead, not to mention recruiting issues. As for Rust's alleged lack of merits, the likes of Discord, Dropbox, Google, Microsoft, Facebook etc seem to think otherwise, not to mention the Linux kernel developers, but of course you are going to set them all straight.

        Comment


        • #24
          There are just additional buzzwords and very little meat in your post.

          Originally posted by jacob View Post
          In detail it's exactly what I just said: linear type system.

          Linear is not a buzzword. In computer science, linear type system has an exact, narrow definition. Look it up.
          "exact, narrow definition"? A twenty page academic document that is based on another academic document in excess of 40 pages? If these things are so silly simple as you imply - why are you unable to write two or three words to add some context?

          First: Ignoring buzzworders is a simple but effective way to weed out trolls.

          Second: Do you realize how abusive it is to expect your readers to look up every third word?

          Originally posted by jacob View Post
          Same for orthogonal: in C++, you basically always need to select a subset of the language that you can use at any given time because the various features don't integrate well together. For example you can't throw exceptions from threads or coroutines, you can't use separate compilation units if you use templates, etc.
          The only tiny little piece of fat in your post - is factually wrong.

          Originally posted by jacob View Post
          As for explaining it more, that's obviously futile as you admitted yourself in your 1st paragraph that you were here to troll, not to ask genuine questions and seek actual answers. If you were interested, there is a wealth of peer-reviewed research papers, language documentation and tutorials on the subject.
          You ignore my interactions with user NobodyXu . I am interested in a genuine discourse - but it has to go beyond buzzwords and ivory tower paper quotes - even if peer-reviewed. Actual code is what counts on the metal. I would be genuinely interested in how you back up your claims from the previous paragraph ("because the various features don't integrate well together", "you can't throw exceptions from threads" and "can't use separate compilation units if you use templates" and the "etc.")

          Comment


          • #25
            Originally posted by lowflyer View Post
            There are just additional buzzwords and very little meat in your post.


            "exact, narrow definition"? A twenty page academic document that is based on another academic document in excess of 40 pages? If these things are so silly simple as you imply - why are you unable to write two or three words to add some context?

            First: Ignoring buzzworders is a simple but effective way to weed out trolls.

            Second: Do you realize how abusive it is to expect your readers to look up every third word?
            Well it just happens to be one of the defining features of Rust. So in any half-serious discussion about Rust it's only natural to assume that people know what we're talking about, especially if someone is so certain of his knowledge that he immediately calls everyone who uses or develops the language a "cargo cult" and "rustafareans". If you don't know how that works and you can't be bothered reading it up, then it's your problem I'm afraid, don't expect other people to spend time helping you to learn after you abused them.

            Originally posted by lowflyer View Post
            The only tiny little piece of fat in your post - is factually wrong.


            You ignore my interactions with user NobodyXu . I am interested in a genuine discourse - but it has to go beyond buzzwords and ivory tower paper quotes - even if peer-reviewed. Actual code is what counts on the metal. I would be genuinely interested in how you back up your claims from the previous paragraph ("because the various features don't integrate well together", "you can't throw exceptions from threads" and "can't use separate compilation units if you use templates" and the "etc.")
            So here's an example for you. Let's say you have 3 tasks to perform asynchronously, each returning an int for simplicity's sake. In C++ you could do something like this (disclaimer: not actually tested):

            Code:
            ​​
            int process_stuff(input_data &d) {
            ...
            }
            
            auto fut1 = std::async(process_stuff, data1);
            auto fut2 = std::async(process_stuff, data2);
            ​auto fut3 = std::async(process_stuff, data3);
            ​
            auto r1 = fut1.get();
            auto r2 = fut2.get();
            auto r3 = fut3.get();
            In Rust, the same code would look like this:

            Code:
            async fn process_stuff(d: &input_data) -> isize {
            ...
            }
            
            let f1 = process_stuff(&data1);
            let f2 = process_stuff(&data2);
            let f3 = process_stuff(&data3);
            
            let (r1, r2, r3) = join!(f1, f2, f3);
            Now what if in C++ process_stuff() throws an exception, or uses some 3rd party library function that does? The whole program will crash. So this is an actual example where you can use async, OR you can call functions that throw exceptions, but not both at the same time.

            In Rust, on the other hand, error management uses the Result<T,E> type which is ultimately a value like any other. If it's thread safe, then it can be sent across threads. So if process_stuff can raise errors, the same situation in Rust would look like this:

            Code:
            async fn process_stuff(d: &input_data) -> Result<isize, SomeError> {
            ...
            }
            Each of the resulting values would then be of type Result<isize, SomeError>, that is either Ok(some_integer_value) or Err(SomeError). Not only it doesn't misbehave, you can also immediately see which of the async tasks succeeded and which failed, and what the error was.

            And what if the data we return is something that is not thread-safe, or the error definition is not thread-safe? Then the program would not compile at all.

            Comment


            • #26
              Originally posted by piotrj3 View Post

              well cryptographic guarantees for safety are requiring safety on such levels as power measements attacks, timing attacks etc. Rust when provides safety in some areas it cannot compete with extreme amount of audits projects like openssl has in manners of security.
              Think twice before picking Rust🤔
              https://mdwdotla.medium.com/using-ru...e-42ab823d9454

              Comment


              • #27
                Originally posted by Nth_man View Post

                Think twice before picking Rust🤔
                https://mdwdotla.medium.com/using-ru...e-42ab823d9454

                That's just like any language, there's pros and cons to use it in a soecific scenarios.

                For example, you won't use C/C++ to write a web server unless you care more about performance (latency) really matters than dev time, or for other reasons etc.

                Comment


                • #28
                  Originally posted by Nth_man View Post

                  Think twice before picking Rust🤔
                  https://mdwdotla.medium.com/using-ru...e-42ab823d9454

                  When i agree with article in principle (Rust is not tool for everything just pick best tool for your job), article itself is quite garbage.

                  Mentioned Async/await - yea of course it is rather new in Rust. But it is already functional and working. Meanwhile in most languages compared to, it doesn't exist at all as part of language so what is a point here? Documentation on new thing that doesn't even exist in competition is maturing?

                  Rust has big learning curve - true. But not bigger then let's say C++. Also people like to overexstimate that. Karol Herbst who wrote opencl driver in Rust, he did that (according to him) in 3 months and took it as learning project for Rust. And you could say writing something like that is waaaay harder then writing stuff article author said.

                  Basically, the problems that Rust is designed to avoid can be solved in other ways — by good testing, good linting, good code review, and good monitoring​
                  And that part is absolutely wrong. This is not problems solved, Memory related issues, multithreading issues are absolutely not easy to solve by things mentioned above. It is very easy to make a memory bug that will sneak through all of that and will one day destroy production. Also code review, testing are quite prone to tunnel vision - if you spot a mistake you tend to not see another bug next to it. Having safer program by design means you can focus in testing, reviewing on things that do really matter like business or logic errors. Rust makes people largely forget about memory errors (only with unsafe you sometimes look at it), and large chunk of logic errors can be eliminated too.

                  Comment


                  • #29
                    Originally posted by jacob View Post

                    Well it just happ... <snip>... not necessary to repeat everything ... <snip/>
                    You delivered *exactly* what I expected: A hodgepodge of snippets and buzzwords. Not a tiny piece of working code. Not even the rusty snippets. So I created a working example for you. ("working example" has an exact, narrow definition. Look it up.) You need to click on the link. It opens the godbolt.org website where you can write and execute code without having to install the compiler.

                    As I said before, your statement is wrong, you can have exceptions in threads and it does not crash. It prints the exact failure of the second thread while the others are executed normally. Also note that the C++ code looks strikingly similar to the rust snippets. No wonder, it does *exactly* the same thing.

                    However, I was not able to get the rust snippet to work. (Yes, rust is likewise on godbolt.org) I guess you know a rust expert you can ask to help you make it work. Just a remark: the rustc compiler spits out this error message:
                    Code:
                    error[E0658]: use of unstable library feature 'future_join'
                    Why do you bring up unfinished features in such discussions? it can backfire dramatically.

                    The next topic is: "various features don't integrate well together". I'm eager to see you explaining it. But please, put in a little bit more effort on the coding side.

                    Comment


                    • #30
                      Originally posted by lowflyer View Post
                      However, I was not able to get the rust snippet to work. (Yes, rust is likewise on godbolt.org) I guess you know a rust expert you can ask to help you make it work. Just a remark: the rustc compiler spits out this error message:
                      Code:
                      error[E0658]: use of unstable library feature 'future_join'
                      Why do you bring up unfinished features in such discussions? it can backfire dramatically.
                      The future join is provided by crate futures or futures-utils.
                      Replace that import of std with one of them will work.

                      Rust's std is very strict on when a feature can be merged since std guarantees backward compatibility, so crates like futures and futures-util provides extensive set of APIs while std only provides the basic Future trait impl plus a few extension APIs.

                      And you would also need an async runtime in Rust, I recommend to use tokio.
                      If you are using tokio, you can simply write tokio::join!(f1, f2, f3).

                      And you didn't complete process_stuff

                      Comment

                      Working...
                      X