

























April 16, 2026, 6:38pm 1
Hi. I am a beginner. Please don't harass me for the sake of my mental health. It's already going down very quickly since the day I learned about Rust.
I have two questions. I think that these two may be related.
I once heard that vertical code is easier to follow along. In other words, one should ignore nesting as much as possible. I believe everyone would agree on this.
Consider the following example from a C++ like language:
void do_something() {
if (condition) {
// do something
} else {
return;
}
}
A cleaner version of the above can be:
void do_something() {
if (!condition) return;
// do something
}
I personally prefer this style and am used to it.
But it looks impossible to do in Rust.
I mean, every here and there we have let Some(x) = Option<_> and it just needs to have those curly braces.
Then we have the question mark (?) operator. As we all know, it is used to propagate errors.
Suppose we have 10 functions. Second function calling the first one, third one calling the second one, and so on...
Suppose only the first function gives a meaningful error and all other functions just propagate it to higher-number functions.
So the last function would just know that there is some error occurred, but it's just too deep. So, it will require keeping too much context in mind.
As an API user, I don't want errors corresponding to very deep functions.
In my opinion, it would be better if we have error message for each of those functions.
So, here, again comes issue, how to make Rust vertical?
It would help a lot if you gave concrete snippets of code you don't like. It's hard to know where to begin without knowing where you're at.
As for error handling, you can... handle errors? If function 2 has a better way to report the error from function 1, then it's on you to make function 2 do that (perhaps with map_err).
2 Likes
Rather than aiming to make your code vertical, you should aim to make it as much readable as possible.
Your code is meant to be read by at least one human: yourself. Never forget that.
5 Likes
The equivalent would simply be:
fn do_something() {
if !condition {
return;
}
// do something
}
Likewise, it's common to see:
let Some(whatever) = option else { return /*or `break` or `continue`*/ };
As already mentioned, for handling errors, when possible, I use result.map_err(|err| err.add_context())? instead of just result?. Sometimes, the added context would need ownership over some !Copy variable that I still need to use in the success case, in which case I do:
let success = match result {
Ok(success) => success,
// This avoids needing to call `Clone::clone` or similar on `owned_value`.
Err(err) => return Err(ErrorWithContext(owned_value, err)),
};
8 Likes
kornel April 18, 2026, 1:22am 5
? propagating errors without context can be a problem, but is fixable.
There's .ok_or(Error::Foo) that converts Option to a Result with an error type that can carry more context. For results there's .map_err(…) that gives you an opportunity to add more context to an existing error.
There's a bunch of similar helper methods and you can have a very tight control over this if you want, by using different error types.
If you don't want to write your own boilerplate for custom errors, there are libraries like anyhow that do it for you and let you write .context("whatever") on any Option or Result, so you can provide all the context to build breadcrumbs of what went wrong.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。