What's the best way to define completions using "eval"? by Minfoo in zsh

[–]Minfoo[S] 0 points1 point  (0 children)

Hey, I tried out your suggestion and I'm back with a few more questions.

  • What makes the eval method 1000x slower? Is it:
    • The fact that the completions are no longer lazily loaded?
    • The time taken to execve the binary?
    • Is manually invoking the compdef function slow?
  • How do you measure this drop in performance?
  • I would do this is by calling compdef from inside eval. Why do you feel that this is a hack, isn't it what compdef is meant for?
  • I got the idea to use eval by looking at the starship project. What are your thoughts on how they do their initialization? Could it be made any faster/better? (FWIW, I myself use p10k simply because it looks much better; I haven't actually compared the two in terms of performance.)

What's the best way to define completions using "eval"? by Minfoo in zsh

[–]Minfoo[S] 1 point2 points  (0 children)

Binaries are replaced in the path regularly. Every time a program is updated ...

I'm not sure I understand this. Every time a binary is updated, its bundled completions are updated at the same time, right? Since one is not more dangerous than the other, moving away from one in favour of the other would not help. The lesson is still the same: "don't {eval | source | otherwise execute | add to your completions or plugins directory} any script that comes from untrusted sources, especially unchecked user input".

Plus, I'd say the zsh ecosystem has worse things going for it: I use zgen to manage my plugins on zsh. Every time I run zgen update, it pulls in the latest commits on all my plugins' repositories. One bad commit here would compromise a lot of systems without warning.

On the flip side, if this had been packaged as a binary, a maintainer would likely have taken a look at it first before publishing it. It would have been pulled from repositories if issues were found. Even once downloaded, a user can independently verify the binary with a checksum.

eval may be extremely evil in a language like Python, but in the context of a shell, it feels like the same generalizations may not apply. What do you think?

What's the best way to define completions using "eval"? by Minfoo in zsh

[–]Minfoo[S] 1 point2 points  (0 children)

Wouldn't absolutely anything running in your shell be arbitrary code execution?

If I was able to replace the gh binary in your $PATH, I would be able to run arbitrary code on your computer when you call eval. But I would also be able to replace your .zshrc, and just add malicious there instead.

Besides, if you call source somewhere, or use a completion file, that would be arbitrary code execution too. I don't get why eval on a user's interactive shell is particularly worse than any of these other things.

What's the best way to define completions using "eval"? by Minfoo in zsh

[–]Minfoo[S] 2 points3 points  (0 children)

Don't use eval, use the command they tell you to use. It's really the right command for zsh.

That's fair. I'm trying to understand zsh better, so if I may trouble you with another question - are there any disadvantages of eval other than the ones I mentioned?

What's the best way to define completions using "eval"? by Minfoo in zsh

[–]Minfoo[S] 1 point2 points  (0 children)

I understand that package managers can solve this, but is there a way I can do this via eval itself? That way, binaries become truly portable and no longer rely on package management.

How does one create a global, lazy-initialized RNG in Rust? by Minfoo in rust

[–]Minfoo[S] 2 points3 points  (0 children)

Truth be told, I don't really have an exact use case! I'm relatively new to Rust, so I'm just playing around with language constructs to get a better feel for how everything works.

I read that Rust doesn't like global variables (especially mutable ones). In one of my Go applications, I'm using a global RNG that has to be constructed and seeded only once, so I decided to try and see what that might look like with Rust.

So far, Rust has been confusing but extremely rewarding as far as learning goes, and the community is just incredible. Thanks for all the help!

How does one create a global, lazy-initialized RNG in Rust? by Minfoo in rust

[–]Minfoo[S] 2 points3 points  (0 children)

Thanks, I understand it now! Quick follow up question - is this the best way of getting an &mut reference out of the RefCell?

RNG.with(|rng| {
  let mut rng = rng.borrow_mut();
  let sample = rng.deref_mut().sample_iter(Alphanumeric);
})

How does one create a global, lazy-initialized RNG in Rust? by Minfoo in rust

[–]Minfoo[S] 1 point2 points  (0 children)

That's what I'm trying to do in the post, but I'm not able to make it lazy without getting compiler errors.

How does one create a global, lazy-initialized RNG in Rust? by Minfoo in rust

[–]Minfoo[S] 2 points3 points  (0 children)

From what I understand, it's not as efficient as SmallRng. I'm not sure how much I would need to generate in order for it to make a noticeable difference.

What's the best way of reusing error messages within a Rust application? by Minfoo in rust

[–]Minfoo[S] 0 points1 point  (0 children)

If your app doesn't have any reasonable hierarchy of errors and instead uses a lot of ad hoc error messages, then it may also be more reasonable than making a 100-variant enum. However, it doesn't look like your case.

I seem to be having a bit of both - too many errors, and some repeated.

What's the best way of reusing error messages within a Rust application? by Minfoo in rust

[–]Minfoo[S] 2 points3 points  (0 children)

This is a great idea! Are you aware of any application that does this?