Is Rust the right Fit for me?

Hi everybody?
How everybody is doing?
I hope you and loved ones are safe and sound.

Since Rust is a new language and it doesn't have as much libraries and tutorials as other languages/Frameworks I am not sure if I can be successful being a newbie.

However since Rust enforce you to write good and secure code, otherwise it won't compile it, the end product/software would perform better (?) and would have fewer or no bugs compared if it was done on another language, specially by a newbie?
For instance, if I would choose .NET 3 C#, Spring Java, Django, Node/Express or Go.
What do you think?

What about being

I would like to create:
a) Secure Web apps
b) Client-server app running on Windows 10 that we could customize how WinApps would be open. Customizing how the apps would open in location and size on the monitor(s).
Like so, 4 instances of Firefox, 3 instances of VLC, Vscode, Photoshop, etc...
Able to to Save this app locations and size as a layout to recall anytime. On a DB?
Client side would be HTML5/CSS/JS and in the future wasm.
Might be dealing with WinAPI?

I hope you could help me and advice me to go in the right direction/language/framework.

All tutorials, video/youtube tutorials available on Rust would give me enough knowledge to make those A and B softwares mentioned or which Language/framework should I choose?

Thanks.

1 Like

I'm doing rather well. Kinda bored of this pandemic.

IMHO, Rust is a good fit if your top alternative is C or C++. If you don't need either of those, then Rust is probably not the right fit. This is a very broad acceptance criteria, but I feel like Rust's strengths really target the same use cases as C and C++.

If you can can accomplish your goals using Java, C#, NodeJS, or Go, then by all means, do so. There are some areas where any of these choices are just not going to be acceptable, and this is the realm of Rust. Some examples are performance requirements, small memory footprint, predictable latency, writing OS kernels and device drivers, etc.

2 Likes

Unless you're tired of less capable type systems, of course. From my own experience - something is much easier to do in Rust than in Java (not mentioning the Javascript), since I can get much more help solely from the types.

5 Likes

Whichever language (s) you choose, that is exactly the main question you should ask yourself. How you produce code which has less bugs. Most organizations spend most of the time just solving bugs and that kills the fun of programming.

  • For the small parts - use test-driven development (TDD). If you want to have less bugs, I would say at least 80% of your code base should be developed by TDD and be covered by unit tests
  • For larger parts of the system , design for testability and make integration test an important part of
  • For the complete system involve QA and Users in user acceptance tests
    I do like how Rust has put test in the forefront . Unit test is part of the language. Even the documentation is tested. Also it seems the language attracts people interested in having reliable/tested systems: according to this inquiry 80% of people do unit tests in rust (https://www.jetbrains.com/lp/devecosystem-2019/rust/)

For Rust on that regarding you need to understand what advantages it brings that will help you reduce bugs:

  • Static typing : this is an advantage over dynamically typed languages such as Python and Javascript. That will produce less bugs. (Not an advantage over other static typed languages like Java, C, C++)
  • Safe memory management: This is a core advantage of Rust, not because other languages do not reach it but because of the way Rust reaches it. Rust does it memory safe and performant. C and C++ are performant but not memory safe (some constructs are but not enforced to use them) . This is what makes "IMHO, Rust is a good fit if your top alternative is C or C++.", mentioned by parasyte. Other languages like Java or Go will manage memory via a garbage collector, which leads to performance issues. So you can think of them as almost memory safe like Rust but not as performant.
  • Safe multithreading - Nowadays multithreading is almost a must because of the way modern processors works. As part of the way Rust manages the memory it also avoid data races. That is pretty unique, I think. And it is awesome (removes hard to find and hard to reproduce bugs)
  • Pedantic and helpful compiler: a pedantic person is very annoying characteristic. But "pedantic" is a very good characteristic for a compiler. Rust strictly disallows you to do some arts of bad code. And that is an awesome thing. At the same time it has really good messages which almost feels like it could have fixed the issue for you alone.

Wow, that is a lot of requirements. I am afraid you will not find a single language which works as silver bullet in all these environments.
You described yourself as being a newbie. Was that only in relation to Rust or for programming in general? Because language choice is a hard problem even for experienced software architects.
Perhaps you should consider if you should simply use the most "default" language for each task, like C# for the Windows part, or Java for the secure web apps.

Rust can definitely fit in some places too. Like you said "in the future wasm"

1 Like

And so, my fellow Crustaceans: ask not if Rust is fit for you — ask if you are fit for Rust.

5 Likes

Thanks guys.

1 Like

Hi everybody.

I did some research, saw several tutorials on Rust and found out Microsoft implemented the WinRT for Rust. With that will be able to control .exe/dll apps inside Win10?


Since most Winapps are C/C++, is there any advantage using C# over Python or Rust, controlling how and where WinApps will be open/close, behave inside windows?

In case Rust is not a good fit, have mature libs for dealing with that yet, for developing a software like that, which languages will have have the features/libs to do that? Python, Node.js, Go?

Software:
b) Client-server app running on Windows 10 that we could customize how WinApps would be open. Customizing how the apps would open in location and size on the monitor(s).
Like so, 4 instances of Firefox, 3 instances of VLC, Vscode, Photoshop, etc...
Able to to Save this app locations and size as a layout to recall anytime. On a DB?
Client side would be HTML5/CSS/JS and in the future wasm.
Might be dealing with WinAPI,WIN32,Windows UIAutomation?

Do you know someone who could help me/teach do such a software and the costs involved?

Thanks.

Yo seem to be repeating the question from your first post.

Given that announcement about the Windows Rust library is only from May I guess you know as much about it as most people.

From the description in the article I guess you just have to install the public preview and see what it can do. From the sound of it it has all Win API's covered.

As described I am looking to hire someone to develop this software.

Sorry I missed the "hire" part of the question.

No problem.

If you can afford GC and don't want to lose your type-level powers, Haskell fits nice here. It's got even more bells and whistles than Rust (for better or worse).

I completely agree with the over-all message though, the type-level programming makes high-level tasks so much simpler. It's hard to lose those tools in something with a restrictive type-system like Golang, where interfaces, reflection, and byte-slices are the only way into polymorphism, or the everything-is-polymorphic-until-the-runtime-crash of JavaScript.

The only high-level abstraction I'm ever willing to give up type-level programming for is Lisp-Style macros. It's a shame hackett, which I believe aimed to provided both, never ended up taking off.

I wish I even knew what 'type level programming' was.

With decades of programming under my belt in all kind of companies and teams, in all kind of application areas from embedded to desktop to 'cloud', in various different countries, and I have never heard anyone talk of 'type level programming'

There seems to be some parallel universe of programmers out there, so far is space and time we can never contact them or know what they are like.

Or is it like that mythical race of COBOL programmers? Which I came to believe was, well a myth, until I actually met one for the first time a year or so ago.

:slight_smile:

More seriously: What is 'type level programming' in a nutshell.

I'd think of it as a way to construct program so that at least some logically incorrect programs (i.e. ones which would crash or give incorrect results at runtime) won't typecheck.

The usual link: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/

My interpretation: once you know something, record that fact in the type system.

For example, don't deal in Vec<u8>s that you "know" are UTF-8 because you ran is_utf8 against, because that's a great way for people to end up both not checking it often enough and also checking it too much because there's no good way to know that someone else has already checked it. Instead, make an API like str::from_utf8 that returns a type that conveys the fact that, yes, it's definitely UTF-8. And then it's obvious whether it's been checked already, and thus whether you need to do so yourself.

Similarly, don't just have a T foo; field and an object lockObj; field where you need to remember to only ever look at foo inside a lock(this.lockObj){ ... } block (C#). Make it Mutex<T> foo and the types keep you from looking at it without locking it, and when you have a MutexGuard<T> already you know that you don't need to lock it again, helping to avoid deadlocks.

EDIT: The post following mine has a good point about the other side of this: Write your code so that other people pass you what you need, not something that might possibly be what you need. So in my example from earlier, if you only support UTF-8, take a &str -- don't take a &[u8] and call assert!(from_utf8(x).is_ok()) or from_utf8(x).unwrap(); don't even from_utf8(x)?. Make the caller give you what you need.

3 Likes

In Rust I think it translates to using the type system to properly validate at compile time.

Say you're writing an API for building HTML document, and you create structs to represent each tag type, and each implementation has an add_child() function. Now you tell each add_child() function to accept a trait with bounds that only allows subtags according to the HTML spec.

I.e it's the rust compiler that will stop you from generating broken HTML documents (well, at least with regards to which children can be added to a tag type), and you don't need to validate that in runtime.

2 Likes

TL;DR

Is it possible to ever get an explanation of theses ideas without having to learn YAFL, in this case Haskell, to understand it?

I promised my self a few years back never to learn yet another programming language, having wasted so much time and mental effort been on so many over the decades. I broke my promise by getting into Javascript a while back, and now Rust. I have to draw the line somewhere.

What you describe there sounds a little bit like Ada. In Ada one can create ones own types as specializations of other types, for example an integer which can only hold a range of values from 100 to 300.

One of the first bugs I found when testing a new aircraft control system was caused by this. When I wound up an actual physical voltage input to the system in a test rig it crashed and burned. Why? Because they had used such user defined types throughout and Ada dutifully panicked when it saw an out of range. It was then I realized they had not range checked any input from anywhere!

Some months later... I got to retest the system.

This just sounds to me like the usual "if you write unwrap you shouldn't be surprised when it panics", not anything bad to do with having specific types. (Or that they just unsafely converted a sensor input into a domain-restricted type without checking. I assume that Ada doesn't just coerce such things willy-nilly.)

Rust does do quite a lot of these type level programming things. The basic idea is that if a value has some type, then you know something about that value. Some examples:

  • We have String that wraps Vec<u8> with the guarantee that it is valid utf-8.
  • We have NonZeroI32 with the guarantee that it is non-zero.
  • We know that an u32 is never negative.
  • We didn't add null to the language, so we do not have to check for that possibility.
  • The &mut T type comes with the knowledge that it is the only active reference.
  • The &T type knows that the value it points at will not change.
  • The &Cell<T> type knows that there are no references to the same value in other threads.
  • The Pin type allows us to add extra information to a reference, namely that we know that the pointee will stay put until it is dropped.

To give the example from the Haskell based post, we could define a NonEmptyVec.

pub struct NonEmptyVec<T> {
    first: T,
    vec: Vec<T>,
}

impl<T> NonEmptyVec<T> {
    pub fn new(mut vec: Vec<T>) -> Option<NonEmptyVec<T>> {
        if vec.is_empty() {
            None
        } else {
            Some(NonEmptyVec {
                first: vec.remove(0),
                vec,
            })
        }
    }
    pub fn push(&mut self, t: T) {
        self.vec.push(t);
    }
    pub fn first(&self) -> &T {
        &self.first
    }
}

playground

In this case, we certainly know that it is non-empty, and the first method can never fail because of this. Alternatively, you can use the unsafe based approach, which is what String does.

pub struct NonEmptyVec<T> {
    vec: Vec<T>,
}

impl<T> NonEmptyVec<T> {
    pub fn new(vec: Vec<T>) -> Option<NonEmptyVec<T>> {
        if vec.is_empty() {
            None
        } else {
            Some(NonEmptyVec { vec })
        }
    }
    pub fn push(&mut self, t: T) {
        self.vec.push(t);
    }
    pub fn first(&self) -> &T {
        match self.vec.get(0) {
            Some(t) => t,
            None => unsafe { std::hint::unreachable_unchecked() },
        }
    }
}

playground

You can also propagate this knowledge that the vector is empty in some cases, e.g. you might have:

impl<T> NonEmptyVec<T> {
    pub fn len(&self) -> NonZeroUsize {
        unsafe {
            NonZeroUsize::new_unchecked(self.vec.len())
        }
    }
}
4 Likes