Rewriting Qt in Rust
-
@JoeCFD said in Rewriting Qt in Rust:
@JonB Do not need to wait for that long. Take a look at
https://www.ibm.com/support/pages/ibm-rational-rose-enterprise-7004-ifix001
A great (but expensive) tool. Not a lot of C++ code needs to be written. I tried it before and really loved it. Unluckily, I could not afford it.In 40 years I am going to use ChatGPT-80 to do all the stuff for me :-) After it resurrected me...
-
@JonB said in Rewriting Qt in Rust:
@SimonSchroeder understands Qt signals and thread affinity perfectly well! :) [Well I think so anyway!]
Thank you for jumping to my rescue!
@TomZ said in Rewriting Qt in Rust:
It feels like you might just not have understood signals and QObject thread affinity if you think this is a problem, really.
Maybe, it is a little related to my programming style. We have a huge old source that transitioned between several GUI frameworks. With the last transition from wxWidgets to Qt there was not enough time to rewrite everything (there never is). The easiest migration path was to just run all the work inside a worker thread. Interspersed within the worker thread are lines similar to
ui->lineEdit->setText(str);
. Yes, I could write a new signal for all those lines and then connect slots. But, why should I add two almost useless lines of code? (Signal definition + connect) Instead, we are using QMetaObject::invokeMethod to put the call into the GUI thread. We even have a little wrapper to writeguiThread([this,str](){ ui->lineEdit->setText(str); });
. It would be nice ifui->lineEdit->setText(str);
did all the signal/slot or event loop stuff under the hood. Qt was invented in a time when CPUs had only a single core. The times have changed since then. A good framework should make multi-threading easier for the programmer.@TomZ said in Rewriting Qt in Rust:
If you find any framework of the size we are dealing with that solves it better, please share.
Qt is still the best framework out there. That's why I am using Qt and that's also why I frequent this forum. Otherwise, I'd be gone...
@TomZ said in Rewriting Qt in Rust:
It already is full open source.
If you want to contribute to the official Qt distribution you need to sign an agreement to release your source code both under open source and the commercial version. This is not "normal" open source.
@Pete-Carter said in Rewriting Qt in Rust:
So i stopped learning more C*.
It looks like you have just invented a new term for the hated C/C++. C++ is not meant for writing glorified C code. It is quite easy to stay on the safe side of C++. I've heard that Bjarne Stroustrup is discussing "profiles" as an addition to the standard. These would allow to restrict C++'s functionality. This could be similar to Rust's safe and unsafe mode. Most likely there will be more than 2 profiles if they get implemented.
-
@Pete-Carter said in Rewriting Qt in Rust:
So i believe that C* should only be used to maintain legacy code, new software should be written in Rust. I see that Rust is the best tool to write C++ like software.
Well, your belief is wrong, but more importantly, your argument is also wrong. Such arguments were put forward when Java came to be (popular). Ironically, there were also people who spelled the advent of Java being the demise of the old and generally bad languages (a.k.a. C/C++). It didn't happen, obviously.
-
@SimonSchroeder said:
It would be nice if ui->lineEdit->setText(str); did all the signal/slot or event loop stuff under the hood
Noooo, that would be terrible. I don't want every little ui setter function to have a thread synchronization overhead. Imagine code like this:
ui->lineEdit->setText(str); str = ui->lineEdit->text();
For this to work correctly you would have two blocking thread synchronization points in the setter and getter. That would kill responsiveness of the ui, not to mention things like property animations would grind to a halt. No, thread synchronization should always be explicit, so you can decide if you want to pay for it. It currently is, because signals/slots and meta-invokes are documented to be thread safe, and that's good.
There are multithreaded ui frameworks e.g. Windows API and MFC can have a separate message thread per window, but any needed synchronization between threads is up to the user. -
@Chris-Kawa said in Rewriting Qt in Rust:
Noooo, that would be terrible. I don't want every little ui setter function to have a thread synchronization overhead.
I would not want full synchronization as well. What I am saying is that the trick (for the setter) to queue something in the background (which would be allowed to be asynchronous) would already help a lot. Besides
guiThread(...)
we also haveguiThreadMaybe(...)
which first checks if the calling thread is already the GUI thread. In that case we directly execute the code, otherwise we queue it into the event loop. I am not sure if an extraif
to check for the calling thread would be too slow (I doubt it).You are right about the getter. This is a little bit more complicated. What we do here is to create a mutex in the calling thread (preferably also only if the calling thread is not the GUI thread) to synchronize the call (just this single call) to
str = ui->lineEdit->text()
. The call would again be queued into the GUI event loop. This will only block the calling thread, but not the GUI thread. Again, calling from the GUI thread could (and should) have a direct call for the setter.This is all up on GitHub: https://github.com/SimonSchroeder/QtThreadHelper (though I don't think we included the
guiThreadMaybe(...)
part). I have seen queued calls overtaking each other (updating the progress of the progress dialog). This is why we use an extra queue in this library (by default) to order the calls and only dispatch a single call into the GUI event loop. You see that it is already quite complex. If this were hidden from the user, that would be great.In many cases I also wouldn't care much about synchronization. Right now, if I call
ui->lineEdit->text()
from a non-GUI thread there is a high chance to crash. From my point of view we are just accessing a variable from a different thread. I would expect that I could get an old value, but not that it crashes (doesn'tQString
have copy on write?). Certainly, the actualQLineEdit::setText
should only be executed on the main thread. -
@SimonSchroeder said:
I am not sure if an extra if to check for the calling thread would be too slow (I doubt it)
In case of simple getters like
QString someGetter() { return someMember; }
an extra if on itself would double the cost or more. A branch and threading code inside the getter (even if in the unlikely branch) would very likely prevent the function from being inlined, adding more penalty for rarely used feature.Conceptually I don't think threading is a responsibility of a widget class and that functionality should always be external to it, e.g. with the sorts of helpers you mentioned. Not only is that a good separation of concerns but also mandating such a mechanism would make writing custom widgets a lot harder. Imagine a beginner writing their first custom widget. You really don't want them to be playing with threading while they learn basics.
Right now, if I call ui->lineEdit->text() from a non-GUI thread there is a high chance to crash. From my point of view we are just accessing a variable from a different thread
QString is implicitly shared. It could be detaching or currently edited by the user typing in lineedit. It's a complex class with sharing and dynamic allocation. It's not safe in general to access such structures without ensuring single access in some way, either logically or through explicit synchronisation. QString has COW, but it's not thread safe or even reentrant, nor should it (it's too basic for such overhead).
-
@Pete-Carter It is ok to lose an argument sometimes. Do not worry, do not stress. Just remember, Qt is the best.
Next topic: "Rewriting Qt in assembly"
-
@SimonSchroeder said in Rewriting Qt in Rust:
I would not want full synchronization as well. What I am saying is that the trick (for the setter) to queue something in the background (which would be allowed to be asynchronous) would already help a lot.
Unfortunately this is one of those cases where it is extremely tempting to move the work to "someone else", but rational introspection will still result into you having to take the responsibility into your own hands.
Any set of classes that of certain size( has a huge number of methods) will decide that it is completely single threaded in usage. I.e. not re-entrant.
Most re-entrant and thread-safe classes are intentionally kept small (API wise).This is just sane design that most designers reach after trying different approaches. Everything else will create bad code.
In this case I'm afraid you're going to have to admit your clients codebase which requires Qt to break this is;
a) not going to actually solve your problem as it just introduces a ton new ones.
b) create that ton of new problems for everyone else using QWidgets.
c) it is actually cheaper to solve this in your own codebase by smartly refactoring things.:shrug: Sorry to be the bearer of bad news, hope you can make it work.
-
@SimonSchroeder said in Rewriting Qt in Rust:
If you want to contribute to the official Qt distribution you need to sign an agreement to release your source code both under open source and the commercial version. This is not "normal" open source.
That's redefining what "open source" means, though.
It in actual fact is open source. Nowhere is there a requirement in the opensource definition that the original authors need to accept your contributions. -
@Pete-Carter I guess I can understand you. If you want it, go for it. It will take long to make it. Java has its GUI part although it is horrible and I never like it.
-
@Pete-Carter Interesting. Qt was started in 1991.
-
I just was reminded of this discussion when I read a different discussion (https://forum.qt.io/topic/156733/i-m-struggling-to-run-qt-s-c-examples-please-assist). Someone posted there that there are already Qt bindings for Rust: https://kdab.github.io/cxx-qt/book/index.html
This would make it more unlikely that anybody would rewrite Qt in Rust. I didn't know that there are tools for Rust to connect to C++ directly (I always thought this had to go through C instead). However, it would only help to rewrite Qt in Rust if the reverse is also possible: to write C++ bindings for the Qt lib rewritten in Rust. Is that possible? Because otherwise this kind of switch would loose the entire community (or rather the community would split and C++ programmers would just fork from the existing Qt library).
-
@SimonSchroeder I believe we should slowly shift away from using C and C++ entirely and start using fast safe modern languages like Rust.
C++ and C are buggy ancient languages that caused countless problems, losses and vulnerabilities. We are in 2024, not in 19XX.A good read:
https://alexgaynor.net/2020/may/27/science-on-memory-unsafety-and-security/ -
@Pete-Carter
This is all very well, but I'm not sure what your point is. As per the theme of this whole topic (or perhaps the other one @SimonSchroeder linked to), Qt is written in C++ and (I do not believe) The Qt Company is going to alter that. One can try "bindings" for Rust if desired/workable. But each time we have a discussion about "wouldn't Qt be better if it were written in Rust/whatever" the fact remains that it is not and is unlikely ever to be. -
The whole point of me talking about this is to urge the Qt devs, who have great experience in GUIs, to build back better. If Qt keeps clinging to the old buggy tech, they will always face problems, and new, safer, hassle free, more productive frameworks will take over.
If your building block (programming language) is flawed, you will have to change it, so that the whole structure becomes better and stronger.
This is my opinion and i am just sharing it. It's just a suggestion for a better future based on my limited knowledge.
-
Rewriting a huge framework is a huge task and costs money.
And if you rewrite Qt in Rust you will make C++ developers unhappy (C++ is still one of the most important programming languages). And modern C++ is not that bad actually, but somewhat complex. Having Qt as a C++ framework and providing bindings for other languages seems to be a good approach. -
-
@Pete-Carter I guess it is more a demand issue than programming language selection. QML and Qt Widgets are parallel. It is doable.
-
@Pete-Carter said in Rewriting Qt in Rust:
C++ and C are buggy ancient languages that caused countless problems, losses and vulnerabilities. We are in 2024, not in 19XX.
Everywhere I have seen these arguments made, C and C++ have been mixed together in argumentation. Yes, you can write plain C code in C++, but you can also use unsafe code in Rust. And considering the tools and knowledge of C++ 20 years ago, C++ and the prevailing programming style (very similar to Java) made it really unsafe. With modern C++ we have unique_pointer and shared_pointer. With proper style at least we don't have use-after-free anymore. The only thing that is missing is a compiler flag to automatically add bounds checks for every array/vector access. (Already included in Herb Sutter's cppfront.) C does not assist you in writing memory safe code, but C++ certainly does.
I would really like to see the statistics for C++ software that started development in the last 10 years separate from older C++ and especially separate from C.
Herb Sutter has a lengthy statement about this whole discussion himself: https://accu.org/journals/overload/32/180/sutter/
@jsulm said in Rewriting Qt in Rust:
Rewriting a huge framework is a huge task and costs money.
And if you rewrite Qt in Rust you will make C++ developers unhappyThis is a chicken/egg problem: It does not make sense to rewrite Qt as long as a lot of software that is using it (also remember embedded/automotive!) in its majority is written in C++ (especially if it is hard to do C++ bindings for a Rust library). Nobody will move away from C++ if a lot of additional libraries for a project are also C++ (if they are C it is a little easier). At the same time, if Qt would move over first to Rust, it would loose its user base, if it is not compatible with C++. And as I said before, people would just fork the current Qt and keep developing it in C++. You cannot easily break this momentum.
BTW: Every rewrite introduces new bugs! This makes it unreasonable to rewrite such a large framework in another language. Qt from the beginning already has a superior approach to memory handling with QObject and the parent/child relationship. It is really hard to make Qt more memory safe when used properly. (I would really like to have a factory function like
T* QObject::make<T*>(...)
to avoid writingnew
which automatically assigns the parent when creating new objects.)