Skip Navigation

InitialsDiceBearhttps://github.com/dicebear/dicebearhttps://creativecommons.org/publicdomain/zero/1.0/„Initials” (https://github.com/dicebear/dicebear) by „DiceBear”, licensed under „CC0 1.0” (https://creativecommons.org/publicdomain/zero/1.0/)B
Posts
10
Comments
274
Joined
3 yr. ago

Collecting downvotes since before forums even had thank you's.

  • I just looked at the code, and it's actually a neat idea implementation wise, and I see now how it helped you.

    But I'm still of the opinion that its a purpose mismatch.

  • Last week I basically duplicated the serialization code to provide better debug output…

    Why was duplication needed, instead of implementing Debug using serialized output?

    And why would one want to rename stuff...etc in Debug anyway? It's for debugging?

    This looks like purpose mismatch to me.

  • Also:

    A short post on how variable names can leak out of macros

    I don't think you understood the blog OP!

  • Maybe a good idea for a post. But the amount of reaches required makes this icky.

    • Pretending people write:
        rust
          
      let Ok(x) = read_input() else { return Err(Error) };
      
      
        
      instead of
        rust
          
       let x = read_input().map_err(|_| ...)?;
      
        
    • Pretending people write:
        rust
          
       const x: &str = "...";
      
      
        
      instead of
        rust
          
       const X: &str = "...";
      
        
    • Pretending there exist people who have such knowledge of rust macros hygiene, ident namespaces, etc, but somehow don't know about how macro code expands (the "shock" about the compile error).

    Maybe there is a reason after all why almost no one (maybe no one, period) was ever in that situation.

  • Bringing vimperator/pentadactyl back! That would be the dream.

    Anyway, last time I tested it (~3 weeks ago), servo was not very usable still with the few websites I tried. Hopefully it gets, at least partway, there in a few months.

  • 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.

  • 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..

      rust
        
    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 Results 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:

    1. Re-using the spawn terminology here irks me for some reason. I don't know what would be better though. Would defer_to_scope() be confusing if the job is awaited in the scope?
    2. Even if assumed obvious, a note about execution order when there is a mix of awaited and non-awaited jobs is worth adding to the documentation IMHO.
  • 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.

      rust
        
    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!

  • 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.

  • Alright. Explain this snippet and what you think it achieves:

      rust
        
    tokio::task::spawn_blocking(move || -> Result { Ok(walkdir) })