Okay, here’s a thing I like about Rust: Returning things as Option and error handling. (Or the more complex Result, but it’s easier to explain with Option.)

fn mydiv(num: f64, denom: f64) -> Option<f64> {
    // (Let’s ignore precision issues for a second.)
    if denom == 0.0 {
        return None;
    } else {
        return Some(num / denom);
    }
}

fn main() {
    // Explicit, verbose version:
    let num: f64 = 123.0;
    let denom: f64 = 456.0;
    let wrapped_res = mydiv(num, denom);
    if wrapped_res.is_some() {
        println!("Unwrapped result: {}", wrapped_res.unwrap());
    }

    // Shorter version using "if let":
    if let Some(res) = mydiv(123.0, 456.0) {
        println!("Here’s a result: {}", res);
    }

    if let Some(res) = mydiv(123.0, 0.0) {
        println!("Huh, we divided by zero? This never happens. {}", res);
    }
}

You can’t divide by zero, so the function returns an ā€œerrorā€ in that case. (Option isn’t really used for errors, IIUC, but the basic idea is the same for Result.)

Option is an enum. It can have the value Some or None. In the case of Some, you can attach additional data to the enum. In this case, we are attaching a floating point value.

The caller then has to decide: Is the value None or Some? Did the function succeed or not? If it is Some, the caller can do .unwrap() on this enum to get the inner value (the floating point value). If you do .unwrap() on a None value, the program will panic and die.

The if let version using destructuring is much shorter and, once you got used to it, actually quite nice.

Now the trick is that you must somehow handle these two cases. You must either call something like .unwrap() or do destructuring or something, otherwise you can’t access the attached value at all. As I understand it, it is impossible to just completely ignore error cases. And the compiler enforces it.

(In case of Result, the compiler would warn you if you ignore the return value entirely. So something like doing write() and then ignoring the return value would be caught as well.)


#a4qstxa

(#xfrwj3q) @aelaraji@aelaraji.com I use Alt+. all the time, it’s great. šŸ‘Œ

FWIW, another thing I often use is !! to recall the entire previous command line:

$ find -iname '*foo*'
./This is a foo file.txt

$ cat "$(!!)"
cat "$(find -iname '*foo*')"
This is just a test.

Yep!

Or:

$ ls -al subdir
ls: cannot open directory 'subdir': Permission denied

$ sudo !!
sudo ls -al subdir
total 0
drwx------ 2 root root  60 Jun 20 19:39 .
drwx------ 7 jess jess 360 Jun 20 19:39 ..
-rw-r--r-- 1 root root   0 Jun 20 19:39 nothing-to-see

#c6wcloa

(#xfrwj3q) @aelaraji@aelaraji.com Oh, that’s great! I haven’t heard about any of them before either. There’s also a caveat though, that I ran right into the very first time I tried this in zsh:

$ ls > /dev/null
$ echo $_
--color=tty

Yeah, exactly what you think:

$ which ls
ls: aliased to ls --color=tty

Alt+. is going to be my favorite one! In the above, it would also give me /dev/null, which might be probably more what I would expect.


#g6omxpq

(#y36ebpa) I also just noticed that the performance issue doesn’t affect all games. šŸ¤” Sigh, I’ll just downgrade for the time being. Not in the mood to fiddle with this.


#pjoy3pq

(#y36ebpa) @kat@yarn.girlonthemoon.xyz I guess that qualifies as an ā€œArch momentā€, albeit the first one I encountered. I’m running this since 2008 and it’s usually very smooth sailing. šŸ˜…

@lyse@lyse.isobeef.org Yeah, YMMV. Some games work(ed) great in Wine, others not at all. I just use it because it’s easier than firing up my WinXP box. (I don’t use Wine for regular applications, just games.)


#luv4l6q

I probably should implement some editing feature in tt. Sure, I can easily edit my feed in vim to fix typos. But then I still have to manually remove the old message from the cache so that the new message is inserted on next reload and I don’t end up with ā€œduplicatesā€ in the message tree.


#xu7z3za

(#v7d7d4a) @movq@www.uninformativ.de That sounds great! (Well, they actually must have recorded the audio with a potato or so.) You talked about pledge(…) and unveil(…) before, right? I somewhere ran across them once before. Never tried them out, but these syscalls seem to be really useful. They also have the potential to make one really rethink about software architecture. I should probably give this a try and see how I can improve my own programs.


#l4vun6q

Speaking of Wine, Arch Linux completely fucked up Wine for me with the latest update.

  • 16-bit support is gone.
  • Performance of 3D games is horrible and unplayable.

Arch is shipping a WoW64 build now, which is not yet ready for prime time.

And then I realized that there’s actually only one stable Wine release per year but Arch has been shipping development releases all the time. That’s quite unusual. I’m used to Arch only shipping stable packages … huh.

Hopefully things will improve again. I’m not eager to build Wine from source. I’d rather ditch it and resort to my real Windows XP box for the little (retro)gaming that I do … 🫤


#y36ebpa

(#k7hrijq) @prologic@twtxt.net I’m trying to call some libc functions (because the Rust stdlib does not have an equivalent for getpeername(), for example, so I don’t have a choice), so I have to do some FFI stuff and deal with raw pointers and all that, which is very gnarly in Rust – because you’re not supposed to do this. Things like that are trivial in C or even Assembler, but I have not yet understood what Rust does under the hood. How and when does it allocate or free memory … is the pointer that I get even still valid by the time I do the libc call? Stuff like that.

I hope that I eventually learn this over time … but I get slapped in the face at every step. It’s very frustrating and I’m always this šŸ¤ close to giving up (only to try again a year later).

Oh, yeah, yeah, I guess I could ā€œjustā€ use some 3rd party library for this. socket2 gets mentioned a lot in this context. But I don’t want to. I literally need one getpeername() call during the lifetime of my program, I don’t even do the socket(), bind(), listen(), accept() dance, I already have a fully functional file descriptor. Using a library for that is total overkill and I’d rather do it myself. (And look at the version number: 0.5.10. The library is 6 years old but they’re still saying: ā€œNah, we’re not 1.0 yet, we reserve the right to make breaking changes with every new release.ā€ So many Rust libs are still unstable …)

… and I could go on and on and on … 🤣


#uiohq5a

Come on, why is the bloody IBAN only in the damn HTML part of your e-mail but not in the plain text!? Grrr! Don’t you wanna get paid, dealer!? Your new web shop system sucks so bad, I want the old version back.


#vaoesxq

OpenBSD has the wonderful pledge() and unveil() syscalls:

https://www.youtube.com/watch?v=bXO6nelFt-E

Not only are they super useful (the program itself can drop privileges – like, it can initialize itself, read some files, whatever, and then tell the kernel that it will never do anything like that again; if it does, e.g. by being exploited through a bug, it gets killed by the kernel), but they are also extremely easy to use.

Imagine a server program with a connected socket in file descriptor 0. Before reading any data from the client, the program can do this:

unveil("/var/www/whatever", "r");
unveil(NULL, NULL);
pledge("stdio rpath", NULL);

Done. It’s now limited to reading files from that directory, communicating with the existing socket, stuff like that. But it cannot ever read any other files or exec() into something else.

I can’t wait for the day when we have something like this on Linux. There have been some attempts, but it’s not that easy. And it’s certainly not mainstream, yet.

I need to have a closer look at Linux’s Landlock soon (ā€œsoonā€), but this is considerably more complicated than pledge()/unveil():

https://landlock.io/


#v7d7d4a