Option
and error handling. (Or the more complex Result
, but it’s easier to explain with Option
.)
@prologic@twtxt.net I’d say: Yes, because in Go it’s easier to ignore errors.
We’re talking about this pattern, right?
f, err := os.Open("filename.ext")
if err != nil {
log.Fatal(err)
}
Nothing stops you from leaving out the if
, right? 🤔
(Of course, if we’re talking about a project you’re doing for a customer and the customer keeps asking for new stuff, then you’re never done, and you have to think ahead and expect changes. Is that what they mean? 🤔)
Saw this on Mastodon:
https://racingbunny.com/@mookie/114718466149264471
18 rules of Software Engineering
- You will regret complexity when on-call
- Stop falling in love with your own code
- Everything is a trade-off. There’s no “best” 3. Every line of code you write is a liability 4. Document your decisions and designs
- Everyone hates code they didn’t write
- Don’t use unnecessary dependencies
- Coding standards prevent arguments
- Write meaningful commit messages
- Don’t ever stop learning new things
- Code reviews spread knowledge
- Always build for maintainability
- Ask for help when you’re stuck
- Fix root causes, not symptoms
- Software is never completed
- Estimates are not promises
- Ship early, iterate often
- Keep. It. Simple.
Solid list, even though 14 is up for debate in my opinion: Software can be completed. You have a use case / problem, you solve that problem, done. Your software is completed now. There might still be bugs and they should be fixed – but this doesn’t “add” to the program. Don’t use “software is never done” as an excuse to keep adding and adding stuff to your code.
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.)
We really are bouncing back and forth between flat UIs and beveled UIs. I mean, this is what old X11 programs looked like:
https://www.uninformativ.de/desktop/2025%2D06%2D21%2D%2Dkatriawm%2Dold%2Dxorg%2Dapps.png
Good luck figuring out which of these UI elements are click-able – unless you examine every pixel on the screen.
@kat@yarn.girlonthemoon.xyz I might give it a shot. 😃
Skimming through the manual: I had no idea that keeping the “up” cursor pressed actually slows you down at some point. 🤦
@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
@kat@yarn.girlonthemoon.xyz I like the animations in your version much better than the ones from ExtremeTuxRacer. 😊 And there’s no little dance at the end of a race!
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.
@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.)
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 … 🫤
@kat@yarn.girlonthemoon.xyz lol, oof, well, better than nothing. 🥴 It appears to run quite well. 🤔
@prologic@twtxt.net Ahhh, right, my bad, I could have easily found that. 🤦
There’s also a project page which lists some limitations of this study: https://www.media.mit.edu/projects/your-brain-on-chatgpt/overview/
It certainly sounds plausible. “Use it or lose it.”
@prologic@twtxt.net But is there a source for it? Am I too stupid to use that site? 🤪
@prologic@twtxt.net … or just bullshit.
I’m Alex, COO at ColdIQ. Built a $4.5M ARR business in under 2 years.
Some “C-level” guy telling people what to do, yeah, I have my doubts.
@prologic@twtxt.net This doesn’t cite any sources, might as well be satire. 🤔
@kat@yarn.girlonthemoon.xyz Awww. :( Can you tell why? Missing libraries or does it just segfault?
To really annoy my neighbors and everyone in a 5 mile radius, I might take my Model M and type a blogpost on the balcony. 😈
@aelaraji@aelaraji.com I’d love to have a positive, optimistic reply to that, but … uhm … I don’t. 🤣
@kat@yarn.girlonthemoon.xyz Ooh, I’ve got to bookmark that page. 😃
@aelaraji@aelaraji.com I wish I had the luxury of not reading that junk. 😅 But instead, I have a Mutt hotkey that pipes an HTML mail through elinks … Bah.
@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 … 🤣
@lyse@lyse.isobeef.org … because you, me, and that guy over there in the corner are the only three people left using plain-text email. 🫤 (And probably Stallman.)
Fuck me sideways, Rust is so hard. Will we ever be friends?
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()
:
“Learn Something Old Every Day, Part XV: KEYB Is Half of Keyboard BIOS”
https://www.os2museum.com/wp/learn-something-old-every-day-part-xv-keyb-is-half-of-keyboard-bios/
fn sub(foo: &String) {
println!("We got this string: [{}]", foo);
}
fn main() {
// "Hello", 0x00, 0x00, "!"
let buf: [u8; 8] = [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x00, 0x21];
// Create a string from the byte array above, interpret as UTF-8, ignore decoding errors.
let lossy_unicode = String::from_utf8_lossy(&buf).to_string();
sub(&lossy_unicode);
}
Create a string from a byte array, but the result isn’t a string, it’s a cow 🐮, so you need another to_string()
to convert your “string” into a string.
- https://doc.rust-lang.org/std/string/struct.String.html#method.from_utf8_lossy
- https://doc.rust-lang.org/std/borrow/enum.Cow.html
I still have a lot to learn.
(into_owned()
instead of to_string()
also works and makes more sense to me, it’s just that the compiler suggested to_string()
first, which led to this funny example.)
@lyse@lyse.isobeef.org Rust is so different and, at the same time, so complex – it’s not far fetched to assume that I simply don’t understand what’s going on here. The docs appear to be clear, but alas … is it a bugs in the docs? Is it a lack of experience on my part? Who knows.
By the way, looks like there was a bit of a discussion regarding that name:
So I was using this function in Rust:
https://doc.rust-lang.org/std/path/struct.Path.html#method.display
Note the little 1.0.0
in the top right corner, which means that this function has been “stable since Rust version 1.0.0”. We’re at 1.87 now, so we’re good.
Then I compiled my program on OpenBSD with Rust 1.86, i.e. just one version behind, but well ahead of 1.0.0.
The compiler said that I was using an unstable library feature.
Turns out, that function internally uses this:
https://doc.rust-lang.org/std/ffi/struct.OsStr.html#method.display
And that is only available since Rust 1.87.
How was I supposed to know this? 🤨
@bender@twtxt.net Yeah, well, it’s a bit like twtxt. There is a Gopher community, but it’s small. I actually don’t like that HTTP is so easily accessible. I don’t like it that much when people post links to my site on HackerNews or something like that. Too much exposure.
Gopher is a small world. It’s slow and cozy.
And much like twtxt, the protocol is simple®, so it’s easier to tinker with it.
@prologic@twtxt.net Yeah, I’m very glad twtxt/Yarn doesn’t have this. ✌️
@kat@yarn.girlonthemoon.xyz Oh, ah, I didn’t even know they sold boxes. 🤯 I hope it still works!
@quark@ferengi.one It’s as close as coffee as you can get. 😅 They take the beans, apply magic, and then most of the caffeine is gone. You can also buy whole decaf’d beans and then grind them yourself. It does kill some of the flavor – but it’s not like you’re drinking black water.
@prologic@twtxt.net That isn’t really my strong suit. 😅
@thecanine@twtxt.net … all these stupid, pointless “apps” are stuff that I eventually have to remove from family devices … Sigh.
@bender@twtxt.net Both Gopher and Mastodon are a way for me to “babble”. 😅 I basically shut down Gopher in favor of Mastodon/Fedi last year. But the Fediverse doesn’t really work for me. It’s too focused on people (I prefer topics) and I dislike the addictive nature of likes and boosts (I’m not disciplined enough to ignore them). Self-hosting some Fedi thing is also out of the question (the minimalistic daemons don’t really support following hashtags, which is a must-have for me).
I’ll probably keep reading Fedi stuff, I just won’t post that much, I think.
Gopher server is back online and I’ll be phasing out Mastodon.
gopher://uninformativ.de
(No, I won’t do multi-protocol twtxt again. 😅)
@prologic@twtxt.net Yeah, it’s difficult, you often don’t get what you’d expect. They also make heavy use of 3rd party libraries. IIUC, for random numbers, they refer to this library. I’ve read many times that the Rust stdlib is intentionally minimalistic (to make it easier to maintain and port and all that).
I’m struggling with this, using 3rd party libs for so many things isn’t really my cup of tea. I’ll probably make my own tiny little “standard library”. It’s silly, but I don’t see any other options. 🤷
@bender@twtxt.net “Mhhhhhhh, hehehe, gonna poop on that car. 😏”
@lyse@lyse.isobeef.org That’s interesting, I see them (Teichrallen) everywhere I look. 🤯 It feels like they’re about as common as mallards (Stockenten) over here. 🤔
@quark@ferengi.one Plot twist: I only drink decaf. 🤯🤯🤯
Sitting on the balcony with a fucking cup of coffee. https://movq.de/v/463f1f9d03/s.png
@kat@yarn.girlonthemoon.xyz I’m sure he’s doing a good job! 😊
@lyse@lyse.isobeef.org Looks pretty nice! Enjoy the mild temperatures while you can. 😅
@aelaraji@aelaraji.com Thanks mate. 👌 (In the grand scheme of things I’m still doing great. World news are a horrible shit show nowadays, ffs. 😭)
@kat@yarn.girlonthemoon.xyz Welcome back. 👋 (It’s a bit quiet here in general. 🤔)
@lyse@lyse.isobeef.org Thanks. 😅 Quite a few of them waddle around at the pond in our village. But those two individuals were seen in a nearby zoo. They’re not zoo animals, they just live there. 😅
Bird photos of the day:
- Egyptian Goose
- Common Moorhen (half asleep)
- Rock Dove
I wanted to port this to Rust as an excercise, but they still have no random number generator in the core library: https://github.com/rust-lang/rust/issues/130703
That’s the code, it’s surprisingly simple: https://movq.de/v/81dd5649be/
@lyse@lyse.isobeef.org Only 10% of the German population had Internet access in 1998: https://de.wikipedia.org/wiki/Internet_in_Deutschland#/media/Datei:Diagramm_Internetnutzer_in_Deutschland.svg I guess I was lucky in that regard.
(If today’s tech wasn’t constantly trying to track and scam you, I might still be an early adopter.)
@lyse@lyse.isobeef.org Nice! The final desk looks like it’s right out of Skyrim. 😃
Having some fun with SIRDS this morning.
What you should see: https://movq.de/v/dae785e733/disp.png
And the tutorial I used for my C program: https://www.ime.usp.br/~otuyama/stereogram/basic/index.html
@lyse@lyse.isobeef.org Oooooh, never seen that before. 😲 Either white-balance doing funny stuff or unusual “filtering” through those clouds. 🤔
Ctrl+U
to the front or Ctrl+K
to the end puts it in a buffer that can be pasted by pressing Ctrl+Y
! That's neat. Even removing the last word with Ctrl+W
moves it into this paste buffer.
@lyse@lyse.isobeef.org Ctrl-U in Vim does something similar (“Delete all entered characters before the cursor in the current line”), but it does not put them into the “clipboard”. I sometimes hit Ctrl-U by accident and then my text is gone. 😡😂
A bill from our ISP in 1998.
We’re talking about a month here, 1998-07-27 to 1998-08-26.
Basic fee: 7.50 DM (about 6€ today).
Online time: 516 minutes, 23.53 DM (about 20€ today).
That’s just the ISP costs, if I’m not mistaken. The underlying phone calls were pretty pricey as well.
Just as a little courtesy call (is that the right term?): 2025 continues to be annoying and exhausting, and I won’t really have the energy to work on twtxt/Yarn or texudus. Other than the occasional retrocomputing thingy (which gives a nice boost of nostalgia), I’m not doing much of anything lately.
SuSE Linux 6.4 and Arachne on DOS also work (with Windows 2000 as a call target):
@bender@twtxt.net Hm, I was just surprised to see so many non-english feeds in there. 🤔
@lyse@lyse.isobeef.org Like this? 😅
@lyse@lyse.isobeef.org Where do they come from, anyway? Are they somehow auto-discovered or is this a curated list?
@kat@yarn.girlonthemoon.xyz I don’t do a lot of CSS and tried to use flexboxes recently, couldn’t find a great explanation. I somehow managed to get the desired effect, but am I using them correctly? Who knows.
@lyse@lyse.isobeef.org I cannot / could not imagine that, either – but if it’s publicly available on the internet and something links to it, they’ll eventually find, scrape it, use it. ☹️
@arne@uplegger.eu … Petrasilie? :-D
When I chose the MIT license for all of my software, I thought:
“Should I use GPL, which I don’t really understand? Is that worth it? Yeah, there is a theoretical possibility that some company might use my code in their proprietary product … and then what? Should I sue them to enforce the GPL? I’m not going to do that anyway, so I’ll just use the MIT license.”
And now we have those LLM scrapers and now it’s suddenly a reality that these companies (ab)use my code. I can see it in my logs. I didn’t expect that back then.
GPL wouldn’t help, either, of course. (Regardless, I now think that GPL would have been the better choice anyway.)
I’m honestly considering taking my code and website offline. Maybe make it accessible through some obscure protocol like Gopher or Gemini, but no more HTTP.
(Yes, Anubis might help. Temporarily.)
I’m just tired.
Getting a bit quiet in the Yarniverse.
@arne@uplegger.eu Was sind denn Petras? :-)
🌈 Hooray! 🌈
@kat@yarn.girlonthemoon.xyz A blast from the past! 😅 And all of it still works, that’s quite the surprise. I mean, I’m making real phone calls here and let the modems talk over that connection … Almost like in the 90ies. 😅
@nghialele@nghia.im Man, I wish I could watch Formula 1 on a regular basis again, but it has become expensive as fuck here. 🫤
This is my highlight, really, haven’t seen this in action in a loooooooong time:
I had a lot of fun with my modems these past few days:
https://www.uninformativ.de/blog/postings/2025-05-31/0/POSTING-en.html
@bender@twtxt.net Not sure if you’re serious or joking, but: IE3 introduced support for CSS, Mosaic completely ignores it. 😅 Besides, it looks fine in IE3 now as well, after I fixed my CSS bug. 🤪
@prologic@twtxt.net Mosaic (2.7) works fine, I maintain that package in the AUR and test my website regularly. 😅
@quark@ferengi.one Ah, I see. Hm, only problem is, IE 3 doesn’t seem to support this yet. 😅 Nah, I don’t think I’ll go down that road – seems like a slippery slope. 🤣
@bender@twtxt.net Probably, yes. 🤔 There’s no standard way to do that, though, is there? 🤔
… but as it turned out, this was a bug in my CSS. It works now. 🥳
My website is compatible with many old browsers, but Internet Explorer 3, uhm, not so much.
@kat@yarn.girlonthemoon.xyz Yep, can’t wait to hear that dial-up sound again. 😃
@kat@yarn.girlonthemoon.xyz Awww, welcome to the family, little guy. 😅
Maybe you’ll enjoy this as well:
I still have one of my first modems, a Creatix LC 144 VF:
I think this was the modem that I used when I first connected to the internet, but I’m not sure.
I plugged it in again and it still works:
The firmware appears to be from 1994, which sounds about right. I don’t think we had internet access before that. We certainly did use local mailboxes, though. (Or BBS’s, as you might call them.)
I now want to actually use that modem again. For the moment, I can only use a phone to dial into it, I lack a second modem to actually establish a connection. Here’s a video:
Not spectacular, but the modem does answer after me entering ATA
.
I bought another cheap old modem on eBay and am now waiting for it to arrive. Once it’s here, I want to simulate an actual dial-up session, hopefully from OS/2 or Windows 3.x.
@kat@yarn.girlonthemoon.xyz Ah, I see. I would assume that you’ll get used to it at some point. 🤔 But yeah, a lot of meaning is packed into these symbols. (It’s much, much worse with languages like Rust. 😅)
That was so great to watch, I was smiling from ear to ear the whole time. 😃
@kat@yarn.girlonthemoon.xyz In what way should it be more verbose? Can you give an example? 🤔
@lyse@lyse.isobeef.org Oh, yeah, nothing beats modern DVCSs. I just hope that having CVS is better than nothing. We’ll see. 😂
@lyse@lyse.isobeef.org Okay, jetzt hör’ ich’s. :DD
@lyse@lyse.isobeef.org Kenne ich gar nicht und noch sehe ich die Ähnlichkeit nicht, aber kann ja noch kommen. 😅
@bender@twtxt.net With these paper thin walls, it might just work. 🤣
Happy to report that the neighbor has started playing Tschaikowski on their piano. And they’re getting really good at it! This is awesome. 😍
@prologic@twtxt.net That’s an interesting premise in that article:
The fun has been sucked out of the process of creation because nothing I make organically can compete with what AI already produces—or soon will.
This is like saying it’s pointless to make music yourself because some professional player/audio engineer does a better job. Really, there’s always someone or something that’s better than you at a particular job.
If we focus too much on “competition”, then yes, you can just stop doing anything. I don’t know how common this mindset is, especially among artists or creative people. 🤔 I would have assumed that many writers, for example, simply enjoy the process of writing. Am I being too naive once more? 🤣
@prologic@twtxt.net I hadn’t considered this particular scenario, no.
Wanna read something very scary?
Your future doctor is using ChatGPT to pass medical school, so you better start riding a bike and eating healthy now.
😨😨😨
Zum Entsetzen aller Beteiligten, wie auch umstehender Personen und einiger schamfreier Gaffer, welche sich an jenem tosenden Unheil zu ergötzen vermochten, folgte nun des Wochensortiments schrecklichste Geißel: 𝕯𝖊𝖗 𝕸𝖔𝖓𝖙𝖆𝖌.
Und es sollten sich die Wolken teilen, um über ihnen nimmer endende Irrungen und Wirrungen an bovinem Fäkal und fremdgetriebener Lethargie zu erbrechen, auf dass sie zu erkennen gezwungen wären, welche Urkraft der irrealen Zusammenkunft letztlich Herrschaft über sie darstellen sollte: 𝕯𝖆𝖘 𝕭𝖎𝖑𝖉𝖙𝖊𝖑𝖊𝖋𝖔𝖓.
So zogen sie alsbald hin, zu tun wie ihnen geheißen, wohlgleich sie – diesem Schauerspiel trotzend – Trost suchten im einzigen ihnen sicher geglaubten Elixir, das dem Abgrund unter ihnen gleichend tiefschwarz glitzernd Erlösung oder mithin als Mindestmaß Linderung versprach, lag jenes doch in unmittelbarer Nähe befindlich hoffnungsschürend bereit:
K̸͓͙͖̥͗͛ä̷̯̼̤͔̈́f̵̧̿̋͒̈f̷̫̝̖̾̓c̸̛͔̀ḣ̶̳͋̓͊ë̷̫̟́͜͝͝n̵̨̳̬̒?̴̩̈́̄ ☕
@kat@yarn.girlonthemoon.xyz This is way too long for me to watch in its entirety, but: “Ugh, talking is weird” – I feel that, very much. 😅
(Where is there no bass emoji in Unicode? Pah.)
And to finish the day: Om Live at Pioneer Works 🤘 – https://www.youtube.com/watch?v=IwnDKcoVHmY
@lyse@lyse.isobeef.org That bird pic goes right in the wallpaper collection. 😍
Now playing: Funky bass and people moving in a funny way, doing funny faces: https://www.youtube.com/watch?v=zVyEPAMpwDc (Vulfpeck & Chris Thile – Dean Town)
@lyse@lyse.isobeef.org He was a gift. 💚 Hatched in 2007. 🐣 Will be allowed to drive this year.
@lyse@lyse.isobeef.org @kat@yarn.girlonthemoon.xyz Actual family photo:
https://movq.de/v/bfd455ecfe/tux1.jpg
I have another one on my keychain and a small one in the car. And this little guy is probably hard to spot in the photo, because he’s just 1cm tall: