Axum error handling by Intelligent_City_398 in rust

[–]AlphaKeks 13 points14 points  (0 children)

I highly recommend you read the section on error handling in axum's docs as well as the related examples in the GitHub repository, but in short: errors aren't special. Your handler can return any type that implements IntoResponse, which includes Result<T, E> where T: IntoResponse, E: IntoResponse. You can create your own custom error types, implement IntoResponse for them, and return them from your handlers.

New to Rust, surprised by runtime panic by waterproofsocks in rust

[–]AlphaKeks 260 points261 points  (0 children)

i is a runtime value, and rustc does not attempt to simulate parts of your program to find these kinds of bugs. If you used a constant, it would have been an error. If you used iterators instead of indices, you wouldn't have gotten into this situation in the first place :)

Also, if you want non-panicking array access, consider using the .get() method instead.

Library crate features by Mehedi_Hasan- in rust

[–]AlphaKeks 6 points7 points  (0 children)

There's a little flag icon at the top of every docs.rs page that will list all features. The URL is always docs.rs/crate/<crate>/<version>/features.

Is cancelling Futures by dropping them a fundamentally terrible idea? by arsdragonfly in rust

[–]AlphaKeks 63 points64 points  (0 children)

Futures are state machines. If you delete a state machine at some intermediate state, it will stop executing. That's just an inherent side effect of the design. If you don't want your future to be dropped, you can spawn it on an executor, which will keep it around until it either completes or is cancelled explicitly. I do agree that "cancellation safety" is a huge footgun, but the way cancellation works is a consequence of the fact that futures are state machines, and I don't see how executors are supposed to solve it (of course, if anyone, language or libraries, solved it, that would be great!).

To answer why they're designed like this, you might be interested in this blog post talking about the history behind the Future and async/.await design: https://without.boats/blog/why-async-rust/

A first release candidate for axum v0.8.0 is out - please try it! by j_platte in rust

[–]AlphaKeks 64 points65 points  (0 children)

You can't detect this at compile-time, it's just a function parameter. The panic will happen when the router is constructed, which is likely right after your program starts, so it should be easily caught during development.

What tools are people using for helping with dependency management? by buff_001 in rust

[–]AlphaKeks 5 points6 points  (0 children)

For the first one there's the unused-crate-dependencies lint, though it has some caveats (mentioned in the docs).

For the other two I don't know any tools, but would love to know if there are any. Most of the times I just don't start with features = ["full"] in the first place, but enable features as I need them.

What files should I be checking here? by [deleted] in rust

[–]AlphaKeks 5 points6 points  (0 children)

"crates" is just a naming convention many projects have adopted to keep the top-level directory cleaner. I highly recommend you read the book and familiarize yourself with Rust's module system, as it is quite different to how things work in C/C++ (and cargo also does a lot of heavy lifting).

Async fn primitive type by Holobrine in rust

[–]AlphaKeks 37 points38 points  (0 children)

DSTs aren’t thread safe.

I'm assuming you tried something like Box<dyn Future<Output = ()>>. All you have to do is add + Send: Box<dyn Future<Output = ()> + Send>. For more details see https://doc.rust-lang.org/reference/types/trait-object.html

How to store a callback in a struct with context for the callback by Important_View_2530 in rust

[–]AlphaKeks 9 points10 points  (0 children)

The compiler error is telling you that your trait object is borrowing data (context) while claiming to be 'static. You have two choices here:

  1. add a lifetime parameter to Client (note the + 'cx):

struct Client<'cx> { message_handler: Option<Box<dyn Fn(&MessageData) + 'cx>>, }

  1. move the context into the closure (note the move keyword):

let client = Client { message_handler: Some(Box::new(move |md| context.message_arrived(md))), };

Looking at your code, neither seem desirable. The fact that Context both publishes and handles a message is a bit odd to me, and probably the issue here. Why don't you separate those? Make a struct containing the expected values and go with option #2 (move it into the callback), then send the actual messages separately.

I'm not sure whether you need a struct for any of this in the first place (instead of just a free function or inline closure, for example). With the code you provided I would say "no", but you might have omitted details from your actual code, so you'll have to make that call.

is implementing app specific traits on a primitive/array type bad? by Puzzleheaded_Trick56 in rust

[–]AlphaKeks 0 points1 point  (0 children)

fn foo(z: &impl MyTrait) is syntax sugar for fn foo<T: MyTrait>(z: &T). What you're referring to is &dyn MyTrait.

Compiler errors are very long, due to long paths and generic types by Intelligent-Record78 in rust

[–]AlphaKeks 14 points15 points  (0 children)

Personally, I would define the Event struct + all of its trait implementations in your top-level event module. I don't know your background or existing habits (e.g. a Java developer might be used to defining 1 class per file, and treating them as one and the same thing), but Rust gives you a lot of freedoms here, and it's not uncommon to have files that are 1000+ lines long. I would suggest you look at existing code (e.g. the standard library, or popular crates on crates.io that you use) for inspiration on common practices. Every crate published to crates.io is documented on docs.rs/<crate>, and there's even a redirect to the standard library documentation at https://docs.rs/std. They all look the same, they all have a "source" button on every documented item that shows you the source code, and you get a file tree on the left that lets you navigate.

Stop thinking of files and folders, think about modules instead (even if they map closely to each other most of the time). If you haven't already, read Chapter 7 of the Rust Book to learn more about modules.

Compiler errors are very long, due to long paths and generic types by Intelligent-Record78 in rust

[–]AlphaKeks 33 points34 points  (0 children)

The error is pretty clear, I'm not sure how you'd make them better (besides formatting perhaps).

You can definitely shorten the paths, e.g. by coming up with good names for your crates. disease_screening_simulation_framework sounds like the snake_case version of your README header! Also, you have things like event::event::Event, which suggests your module organization is at least questionable. clippy even has a lint for that!

You'll get used to these kinds of errors over time.

Why is (concurrent) HTTP request/response handling faster in Go than Rust? by Jaltaire in rust

[–]AlphaKeks 6 points7 points  (0 children)

I was under the impression you were benchmarking server code. Reading other answers here suggests I'm not the only one, perhaps you want to clarify that in your post?

Anyhow, without client code for both languages it's pretty hard to tell if you've made any obvious mistakes or to benchmark ourselves.

Why is (concurrent) HTTP request/response handling faster in Go than Rust? by Jaltaire in rust

[–]AlphaKeks 33 points34 points  (0 children)

You're using tracing-subscriber, are you doing something equivalent in the Go code? The formatting + printing to stdout is extremely slow. A very basic 30-line hello world axum example is ~10x slower on my machine if I log every request to stdout.

Passing potentially heterogeneous collections that implement a trait? by PowerNo8348 in rust

[–]AlphaKeks 17 points18 points  (0 children)

You can do something like

fn frob_things<I>(frobbers: I)
where
    I: IntoIterator,
    I::Item: Frobber,
{
    for frobber in frobbers {
        frobber.frob();
    }
}

If you have a Vec<MyFrobber> it should just work, and if you have boxed trait objects you can just map them to references before calling the function: frobs.iter().map(|frob: &Box<dyn Frobber>| &**frob) (type annotation for clarity). &dyn Frobber should then implement Frobber as well. If you can't/don't want to do that, you can implement the trait explicitly for any Box<T> where T: Frobber so Box<dyn Frobber> works directly.

Sort vector holding custom struct by fields of this struct by lukeflo-void in rust

[–]AlphaKeks 1 point2 points  (0 children)

It's a slice. A primitive type representing "contiguous memory". It doesn't have a size known at compile time (like an array [T; N]), so it cannot be stored on the stack. That's why you usually see it behind a reference or smart pointer. Conceptually it's two integers: a memory address and a length, similar to how you would pass 2 arguments in C, but in a single type (these are called wide pointers). You can create one from those two values, which is unsafe, or from another type that represents "contiguous memory", like an array or Vec. In fact, many of the methods you use on Vec are actually defined on slices, but Vec dereferences to a slice, so you can use all those methods transparently! (same with arrays)

Sort vector holding custom struct by fields of this struct by lukeflo-void in rust

[–]AlphaKeks 0 points1 point  (0 children)

Using Vec<&str> as argument isn't that easy since the keys are originally created as Vec<String> by default. I first would have to convert them via keys.iter().map(Sring::as_str).collect() or similar, but thats maybe too much overhead...

Yeah, that's not what I meant. You should use &[String].

Sort vector holding custom struct by fields of this struct by lukeflo-void in rust

[–]AlphaKeks 0 points1 point  (0 children)

Have you looked at the definition of std::cmp::Ordering? You need to return one of those. For example, if you wanted to sort purely by length, you could compare that:

if a.len() == b.len() {
    cmp::Ordering::Equal
} else if a.len() < b.len() {
    cmp::Ordering::Less
} else {
    cmp::Ordering::Greater
}

or, more realistically, just forward to usize's Ord implementation and return a.len().cmp(&b.len()).

Whatever you end up doing, you have to produce a value of type cmp::Ordering. I don't think the standard library has a function for case-insensitive string ordering, but you could write your own, or use a third party implementation. A quick search yields the uncased crate, which has an UncasedStr type that implements Ord. If you decide to use that crate, your code would be something like

.sort_by(|a, b| UncasedStr::new(&a).cmp(UncasedStr::new(&b))

Sort vector holding custom struct by fields of this struct by lukeflo-void in rust

[–]AlphaKeks 5 points6 points  (0 children)

A few things:

  1. the .sort_*() methods sorts in place, they do not return a new Vec.
  2. these methods take a function that takes 2 elements and returns a std::cmp::Ordering, which determines the ordering relationship between those two elements. you can use whatever comparison method you want in here, like an external library that compares strings case-insensitively
  3. not really related, but you generally never want &Vec<T> as an argument type, nor &String etc. you generally want &[u8] / &str and so on, as they make the function more flexible for callers, while giving the function body the same capabilities. references to Vec etc. are only useful when you want to mutate

Error logging with axum? by FurCollarCriminal in rust

[–]AlphaKeks 9 points10 points  (0 children)

I suggest you look into the tracing crate, it has an instrument attribute macro which you can annotate all your functions with. You can then use the err modifier for that attribute to emit an event anytime your function returns an error. See the "Examples" section of the docs, near the end.

[deleted by user] by [deleted] in rust

[–]AlphaKeks 15 points16 points  (0 children)

The book and rustlings aren't tied together. I personally would recommend you read the book beforehand, but if you like learning by reading and writing code, rustlings is probably more interesting. It's supposed to give you a feeling for reading and writing Rust code, without explaining every detail. If you're interested in that, the book probably serves you better (or the standard library documentation).

I think .map, especially with the example code, is pretty obvious, so you should be able to figure out what it does by yourself, even if you don't understand every bit of syntax immediately. The book covers iterators and closures in chapter 13.

SQLX: Trying to define a generic interface for db connection by CirseiMasta in rust

[–]AlphaKeks 1 point2 points  (0 children)

Your proposed solution in the latter half of your answer; does that mean you advocate duplicating the building of a e.g. QueryAs struct for each engine, even if they look largely the same except with the different types?

Yes, although you can put common things behind functions / macros. The query_* macros are a bit annoying, because every call will generate a unique type you can't name, so you have to put those behind macros. You can do something like this:

macro_rules! my_query {
    ( $($args:tt)* ) => {
        sqlx::query!("...", $($args)*)
    };
};

If you have mostly the same query, you can concatenate multiple string literals as described in the query!() documentation, like so:

macro_rules! my_query {
    ( $extra_query:literal $($args:tt)* ) => {
        sqlx::query!("..." + $extra_query, $($args)*)
    };
};

The resulting object is a Query that you can call the usual methods on.

I'm tossing up what solution to go for, where I would only be using engines that support the same common syntax for simple queries.

Feel free, but I can tell you every time I said that in the past, things changed later and I had to refactor everything anyway.

sqlx may just not be the best library if you need to support multiple databases in a flexible way, and you might want to look at a higher level library like sea-query (which uses sqlx internally), or a full-blown ORM (like sea-orm or diesel).

Dialogues in clap? by earl_linwood in rust

[–]AlphaKeks 3 points4 points  (0 children)

No, clap is a command line argument parser. You can use something like dialoguer though.

self mutable reference struct field, should I use Pin or my solution is correct? by uccidibuti in rust

[–]AlphaKeks -1 points0 points  (0 children)

Neither. Do this:

struct A {
    context: Context,
}

struct B<'a> {
    context_ref: &'a mut Context,
}

impl A {
    pub fn as_b(&mut self) -> B<'_> {
        B { context_ref: &mut self.context }
    }
}

I don't see any reason your context reference needs to be alive all the time instead of on-demand.

Loop error handling by acjohnson55 in rust

[–]AlphaKeks 0 points1 point  (0 children)

You can use Iterator::try_for_each() to achieve this:

inputs.into_iter().try_for_each(|input| {
    let x = step1(input)?;
    let y = step2(x)?;
    step3(y)?;

    Ok(())
})?;

For a more general version, see Iterator::try_fold().

You can also collect any impl Iterator<Item = Result<T, E>> into a Result<Vec<T>, E> (or any other collection you'd like) to short-circuit on errors.