Hacker News new | past | comments | ask | show | jobs | submit login

> 2. It makes no sense to invest in development of only-for-Rust GUI libraries. Just use in Rust something ready that has stable plain-C ABI. And so people from C/C++, Delphi can use it too. That would be more robust on the long run.

Disagree. Anything that uses a C ABI has to keep track of ownership externally, and that tends to be error-prone; you end up with excessive copying, leaks, or both. And this is a particularly thorny problem for GUIs because, as you say, ownership of GUI objects can be complicated. So it makes a lot of sense to have a Rust-specific GUI library that leverages Rust's ownership-tracking capabilities to make this easier.




UI and BL (business logic layer) can interact by messages.

Think about Protocol Buffers (https://developers.google.com/protocol-buffers ) or JSON/BSON as a core of communication protocol

   // App side:
   window* pw = new Window();

   pw->willReceiveMessage("account.modified", cb1);
   pw->willReceiveMessage("account.closed", cb2);
   pw->willReceiveMessage("account.wantsNew", cb3);
   ....
   pw->loadUI("accounts-view.htm");
   .... 
   pw->fireMessage("account.show", accountData: json)

Messaging, as a concept, is simple and data ownership is crystal clear.


Sure, but at the cost of not being able to share logic between the two layers and giving up most of the advantages of native UI. For example, you can't have an encapsulated component that combines user input with validation that comes from the business logic - you'd have to have an input box that sends a message to the business layer when the user inputs a value and then the business layer does the validation and sends a message back.


> you can't have an encapsulated component that combines user input with validation that comes from the business logic.

Why not? On UI layer I can always do:

    var response = bl.sendMessage("account.validate", accountData);
    if( response && response.valid) ...
    else if( response && !response.valid )
Again, we are doing the messaging for years now, AJAX/REST are messaging protocols ...

I am not saying that this is the only way...

In Sciter you can use custom native UI components extending existing DOM - they can handle event, do custom painting, etc. And you can design those components in Rust, C, Go or whatever you like.


> Why not? On UI layer I can always do:

    var response = bl.sendMessage("account.validate", accountData);
    if( response && response.valid) ...
    else if( response && !response.valid )
Like I said, you're no longer encapsulating the validation in the component, where it logically belongs from a business point of view. You're being forced to distort your architecture for these operational concerns. And it gets worse as your structures and UI get more complicated - e.g. if I want to validate several fields within a single database transaction, that's the sort of thing that Rust's ownership system is ideal for - each datastructure can know how to validate itself given a borrowed connection handle.

> Again, we are doing the messaging for years now, AJAX/REST are messaging protocols ...

Yes, and the overhead of having to serialise everything is the biggest problem with web UI. If you're going to separate the UI from the business logic like that then why even bother making a native UI at all?


I'm actually playing with a message-passing-based UI system right now, mostly as a prototype; long story below. I'm making it considerably more Erlangy than yours -- essentially, you have message-passing with a sequential/direct-style interface on top of it, so one can write code like:

  (send-to foo '(:bar 1 "foo"))
  (setf val (recv))
  (format t "~A~%" val)
  (send-to baz `(:thingy ,val))
The basic idea is, you have some set of primitive widgets (e.g. :text, :image, :text-input), which are "just data," like in HTML. Components, rather than being objects or functions, are instead actors. Other, non-component, actors would exist as well, including a compositor actor associated with each window, which you would be sending messages that look like:

  `(:update (:layout-box (:direction :vertical)
              (:layout-box (:direction :horizontal)
                (:text-input (:layout (= (width :self) (/ (width :parent) 2))
                              :contents "Hello, world!"))
                (:text (:layout (= (width :self) (/ (width :parent) 2)))
                  "Hello, world!"))
              (:include ,other-actor)))
Breaking this down, it creates a layout box (invisible box for layout purposes), containing the equivalent of the HTML:

  <div>
    <input style="width: 50%;" value="Hello, world!"></input>
    <span style="width: 50%;">Hello, world!</span>
  </div>
Below that, it places... whatever OTHER-ACTOR's current render tree is. Other than having its name, the actor sending this tree doesn't need to call into OTHER-ACTOR at all.

The compositor is in charge of gluing together the tree fragments, laying them out (with Cassowary), "legalizing" them into primitives the current platform supports, and displaying them to the screen. It also sends back input events to the actors involved, probably with some CALL-NEXT-METHOD -like primitive to opt into bubbling.

This lets the compositor operate concurrently with state updates to components, and components to operate concurrently with each other. This is good in the general case of "new processors getting more cores, not higher clocks," and especially since I'm going to be using this mainly on ARM devices, where this is especially true, and being able to split computations to work with big.LITTLE is a huge win for battery life.

I think this also should help quite a bit with ensuring a user interface remains interactive while performing lots of work, if I were to implement preemption (or context switch checks often enough to act like preemption is occurring, like Go has historically had). The only bottlenecks where the whole interface can get stuck are in the compositor, and the only expensive thing there is Cassowary. And if that's fast enough for Apple, it's fast enough for me.

Once this is in a state where I can take pretty screenshots (okay, considering my aesthetic taste, not too pretty...), I'll probably submit a blogpost version of this here with code samples. If you wanna discuss this beyond the length HN makes convenient, feel free to drop me a line on https://lists.sr.ht/~remexre/public-inbox (~remexre/public-inbox@lists.sr.ht).

Long story:

I recently got a PinePhone, and I'm experiencing keyboard latency that makes it incredibly annoying to actually type on it. I only use a small handful of apps on my phone, most of which have CLI or library equivalents, so I figured it wouldn't be that much of a loss to reimplement crappy versions, if I at least had a nice keyboard. (Plus, my old phone isn't actually broken yet, so instability here isn't as big of a problem as it might be otherwise.)

So, I reflashed it to a non-graphical build of Arch Linux ARM, and set up SBCL with Swank on it, with the intent of making a new interface to replace Phosh and the apps that run under it. This has the pretty nice effect that I can live-edit code that's running on the phone itself from Vim on any of my other machines (or even several at once, if I want). Plus, Lisp itself has great performance relative to other languages that provide this level of dynamism. Lastly, since it's set up as a systemd service, even when I mangle EGL state enough to get a segfault, it restarts and generally Just Works Right.

I've been meaning to try out this concept, but always meant to implement a custom language that looks more like Erlang semantically to do it with. Using Lisp lets me buck Greenspun's tenth rule, though, and forces me to avoid spending a year implementing my own efficient code generation. Plus, Lisp already runs great on my phone.

I am probably performing the do-notation transform with a macro, though, to allow for the green thread implementation I need for large numbers of actors. Alternately, I could use cl-cont, but I'm not sure that I want continuations exposed to the programmer.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: