The maintenance is too high.
acquired knowledge spotted
The maintenance is too high.
acquired knowledge spotted
I will let you on a little secret.
The best “support” you can get is support from upstreams directly (I’m involved in both sides of that equation). But upstreams will often only “support” you when you 1. run the latest stable version 2. the upstream source code wasn’t patched willy-nilly by the packager (your distro).
So the best desktop linux experience comes with using rolling distro that gives you such packages, with Arch being the most prominent example.
The acquired knowledge that argues stability and tells you otherwise is a meme.
a better solution would be to add a method called something like ulock that does a combined lock and unwrap.
That’s exactly what’s done above using an extension trait! You can mutex_val.ulock()
with it!
Now that I think about it, I don’t like how unwrap can signal either “I know this can’t fail”, “the possible error states are too rare to care about” or “I can’t be bothered with real error handing right now”.
That’s why you’re told (clippy does that i think) to use expect
instead, so you can signal “whatever string” you want to signal precisely.
if you’re really that bothered…
use std::sync::{Mutex, MutexGuard};
trait ULock<'a> {
type Guard;
fn ulock(&'a self) -> Self::Guard;
}
impl<'a, T: 'a> ULock<'a> for Mutex<T> {
type Guard = MutexGuard<'a, T>;
fn ulock(&'a self) -> Self::Guard {
self.lock().unwrap()
}
}
or use a wrapper struct, if you really really want the method to be called exactly lock
.
If lock-ergonomicsⓒ is as relevant to you as indexing, you’re doing it wrong.
I would rather take indexing returning Result
s than the other way around.
One can always wrap any code in {||{ //.. }}()
and use question marks liberally anyway (I call them stable try blocks 😉).
…and that’s how you drive up metrics.
Gross!
Unprofessional.
This is why no one takes Rust seriously.
but futures only execute when polled.
The most interesting part here is the polling only has to take place on the scope itself. That was actually what I wanted to check, but got distracted because all spawns are awaited in the scope in moro
’s README example.
async fn slp() {
tokio::time::sleep(std::time::Duration::from_millis(1)).await
}
async fn _main() {
let result_fut = moro::async_scope!(|scope| {
dbg!("d1");
scope.spawn(async {
dbg!("f1a");
slp().await;
slp().await;
slp().await;
dbg!("f1b");
});
dbg!("d2"); // 11
scope.spawn(async {
dbg!("f2a");
slp().await;
slp().await;
dbg!("f2b");
});
dbg!("d3"); // 14
scope.spawn(async {
dbg!("f3a");
slp().await;
dbg!("f3b");
});
dbg!("d4");
async { dbg!("b1"); } // never executes
});
slp().await;
dbg!("o1");
let _ = result_fut.await;
}
fn main() {
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(_main())
}
[src/main.rs:32:5] "o1" = "o1"
[src/main.rs:7:9] "d1" = "d1"
[src/main.rs:15:9] "d2" = "d2"
[src/main.rs:22:9] "d3" = "d3"
[src/main.rs:28:9] "d4" = "d4"
[src/main.rs:9:13] "f1a" = "f1a"
[src/main.rs:17:13] "f2a" = "f2a"
[src/main.rs:24:13] "f3a" = "f3a"
[src/main.rs:26:13] "f3b" = "f3b"
[src/main.rs:20:13] "f2b" = "f2b"
[src/main.rs:13:13] "f1b" = "f1b"
The non-awaited jobs are run concurrently as the moro docs say. But what if we immediately await f2?
[src/main.rs:32:5] "o1" = "o1"
[src/main.rs:7:9] "d1" = "d1"
[src/main.rs:15:9] "d2" = "d2"
[src/main.rs:9:13] "f1a" = "f1a"
[src/main.rs:17:13] "f2a" = "f2a"
[src/main.rs:20:13] "f2b" = "f2b"
[src/main.rs:22:9] "d3" = "d3"
[src/main.rs:28:9] "d4" = "d4"
[src/main.rs:24:13] "f3a" = "f3a"
[src/main.rs:13:13] "f1b" = "f1b"
[src/main.rs:26:13] "f3b" = "f3b"
f1 and f2 are run concurrently, f3 is run after f2 finishes, but doesn’t have to wait for f1 to finish, which is maybe obvious, but… (see below).
So two things here:
defer_to_scope()
be confusing if the job is awaited in the scope?I skimmed the latter parts of this post since I felt like I read it all before, but I think moro
is new to me. I was intrigued to find out how scoped span
exactly behaves.
async fn slp() {
tokio::time::sleep(std::time::Duration::from_millis(1)).await
}
async fn _main() {
let value = 22;
let result_fut = moro::async_scope!(|scope| {
dbg!(); // line 8
let future1 = scope.spawn(async {
slp().await;
dbg!(); // line 11
let future2 = scope.spawn(async {
slp().await;
dbg!(); // line 14
value // access stack values that outlive scope
});
slp().await;
dbg!(); // line 18
let v = future2.await * 2;
v
});
slp().await;
dbg!(); // line 25
let v = future1.await * 2;
slp().await;
dbg!(); // line 28
v
});
slp().await;
dbg!(); // line 32
let result = result_fut.await;
eprintln!("{result}"); // prints 88
}
fn main() {
// same output with `new_current_thread()` of course
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(_main())
}
This prints:
[src/main.rs:32:5]
[src/main.rs:8:9]
[src/main.rs:25:9]
[src/main.rs:11:13]
[src/main.rs:18:13]
[src/main.rs:14:17]
[src/main.rs:28:9]
88
So scoped spawn
doesn’t really spawn tasks as one might mistakenly think!
Because non-open ones are not available, even for a price. Unless you buy something bigger than the “standard” itself of course, like a company that is responsible for it or having access to it.
There is also the process of standardization itself, with committees, working groups, public proposals, …etc involved.
Anyway, we can’t backtrack on calling ISO standards and their likes “open” on the global level, hence my suggestion to use more precise language (“publicly available and sharable”) when talking about truly open standards.
The term open-standard does not cut it. People should start using “publicly available and sharable” instead (maybe there is a better name for it).
ISO standards for example are technically “open”. But how relevant is that to a curious individual developer when anything you need to implement would require access to multiple “open” standards, each coming with a (monetary) price, with some extra shenanigans [archived] on top.
IETF standards however are actually truly open, as in publicly available and sharable.
The LARPing levels in moronix comments are higher than usual, but the comedic value is still not lost.
Monthly Reminder: High or low, all Linux usage stats are fake.
BTW, the snippet I pointed to, and the whole match block, is not incoherent. It’s useless.
First of all, unsafe
famously doesn’t disable the borrow checker, which is something any Rustacean would know, so your intro is a bit weird in that regard.
And if you neither like the borrow checker, nor like unsafe rust as is, then why are you forcing yourself to use Rust at all. If you’re bored with C++, there are other of languages out there, a couple of which are even primarily developed by game developers, for game developers.
The fact that you found a pattern that can be alternatively titled “A Generic Method For Introducing Heisenbugs In Rust”, and you are somehow excited about it, indicates that you probably should stop this endeavor.
Generally speaking, I think the Rust community would benefit from making an announcement a long the lines of “If you’re a game developer, then we strongly advise you to become a Rustacean outside the field of game development first, before considering doing game development in Rust”.
Alright. Explain this snippet and what you think it achieves:
tokio::task::spawn_blocking(move || -> Result { Ok(walkdir) })
Post the original code to !rust@programming.dev and point to where you got stock, because that AI output is nonsensical to the point where I’m not sure what excited you about it. A self-contained example would be ideal, otherwise, include the crates you’re using (or the use
statements).
Federation is irrelevant. Matrix is federated, yet most communities and users would lose communication if matrix.org got offline.
With, transport-only distributablity, which i think is what radicale offers, availability would depend on the peers. That means probably less availability than a big service host.
Distributed transport and storage would fix this. a la something like Tahoe-LAFS or (old) Freenet/Hyphanet. And no, IPFS is not an option because it’s generally a meme, and is pull-based, and have availability/longevity problems with metadata alone. iroh claims to be less of a meme, but I don’t know if they fixed any of the big design (or rather lack of design) problems.
At the end of the day, people can live with GitHub/GitLab/… going down for a few minutes every other week, or 1-2 hours every other month, as the benefits outweigh the occasional inconvenience by a big margin.
And git itself is distributed anyway. So it’s not like anyone was cut from committing work locally or pushing commits to a mirror.
I guess waiting on CI runs would be the most relevant inconvenience. But that’s not a distributable part of any service/implementation that exists, or can exist without being quickly gravely abused.
for a build of your crate only: single core perf.
Until the parallel compiler feature (-Z threads=<n>
) stabilizes and becomes more complete.
It’s also always worth mentioning that the choice of linker is important. Using mold or lld can significantly speed things up in some use-cases.
Beyond that, codegen-units
and lto
profile options are also important.
And finally, for development purposes, the code generator is important, as cranelift
provides much faster compile times, but resulting binaries are not as optimized as LLVM-generated ones.
If you’re not into tiling, install openbox and a panel of your choosing. You will quickly find that you don’t need a DE at all.