Originally posted by lowflyer
View Post
Announcement
Collapse
No announcement yet.
Fedora 38 Looks To Shift RPM To Sequoia, A Rust-Based OpenPGP Parser
Collapse
X
-
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.
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 PostWell, that's the very simple example.
In real life code get much complicated than that.
Originally posted by NobodyXu View PostOne 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.
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 PostA 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>; }
Originally posted by NobodyXu View PostThat literally would not work if lifetime works like reference counting.
Originally posted by NobodyXu View PostC++ does not have lifetime.
On the contrary, rust embeds lifetime in the type.
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
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 PostFor example:
Code:struct S<T>(T);
So if you create
Code:let i = 1; let s = S(&i);
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
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
-
Originally posted by lowflyer View PostWell, 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"
Originally posted by lowflyer View PostYou 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 PostApart 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*.
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++.
Originally posted by lowflyer View PostI 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"
Originally posted by lowflyer View PostThere 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.
- Likes 1
Comment
-
There are just additional buzzwords and very little meat in your post.
Originally posted by jacob View PostIn 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.
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 PostSame 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.
Originally posted by jacob View PostAs 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.
- Likes 1
Comment
-
Originally posted by lowflyer View PostThere 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?
Originally posted by lowflyer View PostThe 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.")
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();
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);
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> { ... }
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.
- Likes 1
Comment
-
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.
https://mdwdotla.medium.com/using-ru...e-42ab823d9454
Comment
-
Originally posted by Nth_man View Post
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
-
Originally posted by Nth_man View Post
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
- Likes 1
Comment
-
Originally posted by jacob View Post
Well it just happ... <snip>... not necessary to repeat everything ... <snip/>
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'
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.
- Likes 1
Comment
-
Originally posted by lowflyer View PostHowever, 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'
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
- Likes 1
Comment
Comment