Jump to page: 1 211  
Page
Thread overview
A D vs. Rust example
Oct 20, 2022
Don Allen
Oct 20, 2022
jmh530
Oct 20, 2022
Ali Çehreli
Oct 20, 2022
jmh530
Oct 20, 2022
user1234
Oct 20, 2022
IGotD-
Oct 20, 2022
Don Allen
Oct 20, 2022
Don Allen
Oct 20, 2022
rikki cattermole
Oct 20, 2022
Ali Çehreli
Oct 21, 2022
Don Allen
Oct 21, 2022
rikki cattermole
Oct 20, 2022
H. S. Teoh
Oct 20, 2022
Timon Gehr
Oct 21, 2022
Paulo Pinto
Oct 21, 2022
Guillaume Piolat
Oct 21, 2022
Guillaume Piolat
Oct 27, 2022
Quirin Schroll
Oct 27, 2022
ryuukk_
Oct 27, 2022
Imperatorn
Oct 27, 2022
H. S. Teoh
Oct 27, 2022
jmh530
Oct 27, 2022
Timon Gehr
Oct 21, 2022
Paulo Pinto
Oct 21, 2022
Paulo Pinto
Oct 22, 2022
Walter Bright
Oct 23, 2022
Imperatorn
Oct 23, 2022
Sergey
Oct 23, 2022
Imperatorn
Oct 23, 2022
matheus
Oct 23, 2022
Imperatorn
Oct 23, 2022
ryuukk_
Oct 24, 2022
IGotD-
Oct 20, 2022
surlymoor
Oct 20, 2022
Don Allen
Oct 21, 2022
Don Allen
Oct 22, 2022
Walter Bright
Oct 22, 2022
Walter Bright
Oct 22, 2022
Don Allen
Oct 20, 2022
rassoc
Oct 21, 2022
Don Allen
Oct 21, 2022
rassoc
Oct 21, 2022
Don Allen
Oct 21, 2022
IGotD-
Oct 21, 2022
Jack Applegame
Oct 21, 2022
matheus
Oct 21, 2022
IGotD-
Oct 21, 2022
Paulo Pinto
Oct 21, 2022
Imperatorn
Oct 21, 2022
IGotD-
Oct 22, 2022
Walter Bright
Oct 22, 2022
Bruce Carneal
Oct 21, 2022
Walter Bright
Oct 21, 2022
Don Allen
Oct 22, 2022
Walter Bright
Oct 22, 2022
victoroak
Oct 22, 2022
Don Allen
Oct 24, 2022
Dukc
Oct 25, 2022
Don Allen
Oct 25, 2022
Tejas
Oct 25, 2022
Don Allen
Oct 25, 2022
Sergey
Oct 26, 2022
Don Allen
Oct 26, 2022
Don Allen
Oct 26, 2022
Paulo Pinto
Oct 26, 2022
Don Allen
Oct 27, 2022
Imperatorn
Oct 27, 2022
Ali Çehreli
Oct 28, 2022
Ali Çehreli
Oct 26, 2022
H. S. Teoh
Oct 25, 2022
Siarhei Siamashka
Oct 25, 2022
Imperatorn
Oct 30, 2022
Siarhei Siamashka
Oct 27, 2022
Walter Bright
Oct 27, 2022
rikki cattermole
Oct 28, 2022
Walter Bright
Oct 28, 2022
Ali Çehreli
Oct 27, 2022
Walter Bright
Oct 27, 2022
H. S. Teoh
Oct 28, 2022
rikki cattermole
Oct 28, 2022
Walter Bright
Oct 28, 2022
Dukc
Oct 30, 2022
Walter Bright
Oct 31, 2022
Dukc
October 20, 2022

I've mentioned in past messages that I had ported a suite of personal financial management tools successfully from C to D after first attempting the work with Rust. I thought I'd give you an example of one of the many headaches I encountered with Rust, because it's illustrative of my contention that Rust's memory- and thread-safety without a GC makes the programmer a much more active participant in the memory-management system than do languages equipped with a GC. It's a primary reason why Rust is so much more difficult to learn than languages with GC support.

I'm a Scheme enthusiast and have written a lot of it over my many years. A common pattern that is so easy to address in Scheme is a situation where a number of variables get set, usually at the head of a loop, and those variables (sometimes mutable, if they are, for example, Sqlite prepared statements) are needed by a number of functions that are part of the processing. Being able to define closures inside the loop that see those variables as part of their environment makes the code simpler, cleaner, and easier to write, as opposed to passing the needed variables as arguments to the functions at every calling site, C-style.

Rust has closures. Great. So here's an example of an attempt to do something along the lines described above with a single mutable variable:

fn main() {
    let mut foo = 5;
    let mut bar = || {
        foo = 17;
    };
    let mut baz = || {
        foo = 42;
    };
    bar();
    println!("{}", &mut foo);
    baz();
    println!("{}", &mut foo);

}

I have a single mutable variable, foo, and two closures, bar and baz, that each mutate the variable. This solution isn't so bad, yes? Except it doesn't compile:

   Compiling playground v0.0.1 (/playground)
error[E0499]: cannot borrow `foo` as mutable more than once at a time
  --> src/main.rs:7:19
   |
4  |     let mut bar = || {
   |                   -- first mutable borrow occurs here
5  |         foo = 17;
   |         --- first borrow occurs due to use of `foo` in closure
6  |     };
7  |     let mut baz = || {
   |                   ^^ second mutable borrow occurs here
8  |         foo = 42;
   |         --- second borrow occurs due to use of `foo` in closure
9  |     };
10 |     bar();
   |     --- first borrow later used here

error[E0499]: cannot borrow `foo` as mutable more than once at a time
  --> src/main.rs:11:20
   |
7  |     let mut baz = || {
   |                   -- first mutable borrow occurs here
8  |         foo = 42;
   |         --- first borrow occurs due to use of `foo` in closure
...
11 |     println!("{}", &mut foo);
   |                    ^^^^^^^^ second mutable borrow occurs here
12 |     baz();
   |     --- first borrow later used here

For more information about this error, try `rustc --explain E0499`.
error: could not compile `playground` due to 2 previous errors

The problem is that the mutable borrows of foo in each of the closures occur, in effect, when the closure is defined, not when it is called. The compiler views this just as it would two assignments to foo -- two mutable borrows in the same scope -- and refuses to compile the program.

The solution is to use "interior mutability", which I view as a bit of a hack to get around the limitations of compile-time borrow-checking. Interior mutability lets you mutate variables that look immutable to the compiler and the safety of what you do is checked at runtime (so much for "zero cost"). Here's what the code looks like to fix the above using this approach:

use std::cell::RefCell;

fn main() {
    let foo = RefCell::new(5);
    let bar = || {
        *foo.borrow_mut() = 17;
    };
    let baz = || {
        *foo.borrow_mut() = 42;
    };
    bar();
    println!("{}", foo.borrow());
    baz();
    println!("{}", foo.borrow());
    }

This works, but at the cost of readability and some runtime efficiency.

The D equivalent:

import std.stdio;

void main()
{
    int foo;
    void bar() {
        foo = 17;
    }
    void baz() {
        foo = 42;
    }
    bar();
    writeln(foo);
    baz();
    writeln(foo);
}

This is just one of multiple examples I personally encountered where Rust's approach to memory- and thread-safety makes life difficult for its users. On simple cost-benefit grounds, I can see using Rust for writing OS drivers (or even an entire OS) or for use in embedded systems, especially if there's a real-time constraint; in other words, where a GC would be unsuitable. But for ordinary applications on today's hardware? Use of Rust instead of D, Go, Nim, etc. makes no sense to me.

October 20, 2022

On Thursday, 20 October 2022 at 13:37:07 UTC, Don Allen wrote:

>

[snip]
The D equivalent:

import std.stdio;

void main()
{
    int foo;
    void bar() {
        foo = 17;
    }
    void baz() {
        foo = 42;
    }
    bar();
    writeln(foo);
    baz();
    writeln(foo);
}

[snip]

TIL that this works...I thought it would only work for global/static variables.

October 20, 2022

On Thursday, 20 October 2022 at 13:37:07 UTC, Don Allen wrote:

>

[...]
This is just one of multiple examples I personally encountered where Rust's approach to memory- and thread-safety makes life difficult for its users. On simple cost-benefit grounds, I can see using Rust for writing OS drivers (or even an entire OS) or for use in embedded systems, especially if there's a real-time constraint; in other words, where a GC would be unsuitable. But for ordinary applications on today's hardware? Use of Rust instead of D, Go, Nim, etc. makes no sense to me.

Thanks, that's very interesting.

I've remarked that the problem in the first example is that Rust does not seem to detect that the two closures are not escaped, i.e it seems it has no notion of a difference between nested functions and lambdas.

October 20, 2022

On Thursday, 20 October 2022 at 13:37:07 UTC, Don Allen wrote:

>

This is just one of multiple examples I personally encountered where Rust's approach to memory- and thread-safety makes life difficult for its users. On simple cost-benefit grounds, I can see using Rust for writing OS drivers (or even an entire OS) or for use in embedded systems, especially if there's a real-time constraint; in other words, where a GC would be unsuitable. But for ordinary applications on today's hardware? Use of Rust instead of D, Go, Nim, etc. makes no sense to me.

Actually, Rust is even worse for embedded, OS, near hardware programming. The memory model just gets in the way of doing those things. Example of things in OS programming are often intrusive containers, pointers/references pointing in all directions and sometimes even to itself, multiple use of containers for the same object, chicken and egg problems. This is where the memory model of just Rust breaks down. Of course you have to use unsafe everywhere but as soon as you do that, things get ugly very quickly. For these tasks C++ is still the obvious choice. However, this is not normal programming and you have to do tricks that you never usually do in application programming.

In short Rust is a useless language for embedded and it would take forever to become productive. Also information how to do this in Rust is difficult to find as most of Rust programs are just normal applications and that is also what Rust was originally designed for. I've tried several languages in order to evaluate how it can deal with embedded programs (more advanced than just blinking a LED, real resource handling). I've evaluated Rust, D, Nim, Swift among others and Rust got the place at very bottom by a great margin. There were simply things I couldn't do in Rust and even if it possible it would have taken ages do figure out how to do it. The other languages were more easy, not perfect and you had do deal with some quirks but you understood why and how you could work around them.

Just like you I found out how the memory model isn't even zero cost with that you quickly have to resort to reference counting and with that RefCell (which is a runtime check) must be used as you suddenly have several possible paths of mutuable borrows.

For financial software I would run from Rust faster than a rabbit. It's just too complicated and other languages offer faster developing times with more convenient libraries.

I'm puzzled with the high popularity of Rust, like some kind of mass exorcism.

October 20, 2022

On Thursday, 20 October 2022 at 14:26:07 UTC, IGotD- wrote:

>

On Thursday, 20 October 2022 at 13:37:07 UTC, Don Allen wrote:

>

This is just one of multiple examples I personally encountered where Rust's approach to memory- and thread-safety makes life difficult for its users. On simple cost-benefit grounds, I can see using Rust for writing OS drivers (or even an entire OS) or for use in embedded systems, especially if there's a real-time constraint; in other words, where a GC would be unsuitable. But for ordinary applications on today's hardware? Use of Rust instead of D, Go, Nim, etc. makes no sense to me.

Actually, Rust is even worse for embedded, OS, near hardware programming. The memory model just gets in the way of doing those things. Example of things in OS programming are often intrusive containers, pointers/references pointing in all directions and sometimes even to itself, multiple use of containers for the same object, chicken and egg problems. This is where the memory model of just Rust breaks down. Of course you have to use unsafe everywhere but as soon as you do that, things get ugly very quickly. For these tasks C++ is still the obvious choice. However, this is not normal programming and you have to do tricks that you never usually do in application programming.

In short Rust is a useless language for embedded and it would take forever to become productive. Also information how to do this in Rust is difficult to find as most of Rust programs are just normal applications and that is also what Rust was originally designed for. I've tried several languages in order to evaluate how it can deal with embedded programs (more advanced than just blinking a LED, real resource handling). I've evaluated Rust, D, Nim, Swift among others and Rust got the place at very bottom by a great margin. There were simply things I couldn't do in Rust and even if it possible it would have taken ages do figure out how to do it. The other languages were more easy, not perfect and you had do deal with some quirks but you understood why and how you could work around them.

Thank you for your evaluation of Rust for embedded work based on actual experience, which I don't have. I think the lesson of both yours and my experience is that you have to actually try it, get your hands dirty, to understand whether Rust is suitable for both your problem domain and your programming style. You will learn nothing from reading the hype (Exhibit A: the "affectionately named" Book; was this written by a 12-year-old?). I mention the programming style because, for example, someone who had spent their programming life writing C would likely not have encountered the closure issue I did. If all the functions are at top level and everything they need is passed to them as arguments, no problem.

>

Just like you I found out how the memory model isn't even zero cost with that you quickly have to resort to reference counting and with that RefCell (which is a runtime check) must be used as you suddenly have several possible paths of mutuable borrows.

For financial software I would run from Rust faster than a rabbit. It's just too complicated and other languages offer faster developing times with more convenient libraries.

I'm puzzled with the high popularity of Rust, like some kind of mass exorcism.

People (alarmingly frequently) mindlessly jump on band-wagons. We're seeing it in today's politics, but I'll stop right there before destroying this forum :-)

October 20, 2022

Where I do think D would do well by emulating Rust is in the area of helpful compiler warnings: noting unused variables, constants and imported symbols, pointing out unnecessary parentheses, etc.

I do realize that Walter has emphasized compilation speed in dmd and that is a worthy goal. In pursuit of that, some of what I'm suggesting may not be possible with dmd's current level of analysis of the source code. But to the extent that tradeoff exists, I'd suggest re-thinking it. The Rust compiler is much slower than dmd, but I personally did not find its speed (or lack thereof) to be a big issue for me while I was doing Rust development (on contemporary hardware with ~4ghz processors, 32 GB of memory and an NVME SSD). Perhaps there's a cake-and-eat-it solution that would make the additional analysis and resulting warnings optional, so those running on old hardware or care more about compilation speed than the warnings could have it their way too.

October 21, 2022
On 21/10/2022 4:25 AM, Don Allen wrote:
> Where I do think D would do well by emulating Rust is in the area of helpful compiler warnings: noting unused variables, constants and imported symbols, pointing out unnecessary parentheses, etc.

IDE's such as IntelliJ IDEA (D's plugin) call Dscanner all the time to verify code and get warnings.

So in practice this shouldn't be an issue.
October 20, 2022
On 10/20/22 07:09, jmh530 wrote:

> TIL that this works...I thought it would only work for global/static
> variables.

Similar nested function examples appear at the following point during one of my presentations:

  https://www.youtube.com/watch?v=dRORNQIB2wA&t=2419s

Ali

October 20, 2022
On 10/20/22 08:33, rikki cattermole wrote:
> On 21/10/2022 4:25 AM, Don Allen wrote:
>> Where I do think D would do well by emulating Rust is in the area of
>> helpful compiler warnings: noting unused variables, constants and
>> imported symbols, pointing out unnecessary parentheses, etc.
>
> IDE's such as IntelliJ IDEA (D's plugin) call Dscanner all the time to
> verify code and get warnings.
>
> So in practice this shouldn't be an issue.

And there is 'dub lint' that uses dscanner under the hood.

Ali

October 20, 2022
On Thu, Oct 20, 2022 at 03:25:36PM +0000, Don Allen via Digitalmars-d wrote:
> Where I do think D would do well by emulating Rust is in the area of helpful compiler warnings: noting unused variables, constants and imported symbols, pointing out unnecessary parentheses, etc.
[...]

It would be good to file these as enhancement requests to bugzilla, so that they don't get lost in the dusts of forum history.


T

-- 
If you want to solve a problem, you need to address its root cause, not just its symptoms. Otherwise it's like treating cancer with Tylenol...
« First   ‹ Prev
1 2 3 4 5 6 7 8 9 10 11