But there are also differences, for example, errors must be absl::StatusCode, whereas enums in Rust allow for arbitrary error payloads.
Also don't discount ecosystem usage: everyone uses Result in Rust, abeseil isn't used by most things, and std::expected has its own issues (though I can appreciate how tough making those calls is) and only landed in C++23, so it's not as widely used as Result either.
To use a C example, if you add a new definition/variant to an enum, suddenly all switch statements over that enum will fail to compile (unless there is a default: branch).
This does eliminate a large swatch of logic errors, though by no means all.
Almost always you read configuration files at run-time (like sudo does) and change your behavior depending on run-time information - so you will have run-time errors.
In your case, a config object would be mutable inside the function that loads it from disk into memory, then read-only everywhere else by default.
It usually devolves into a lot of nested if-else and switch (match) instructions.
Not saying you haven't done that, just sharing my personal experience with it.
This led to a lot of unnecessary friction with the borrow checker while I was still getting to understand it.
Once I started always asking myself "does this really need to be a ref?", things became much easier. And this in turn revealed itself as a useful rule of thumb for keeping coupling between subsystem in check.
Then I was doing some C work and I realised I was asking the same question to myself about pointers too. And I found it could often reduce cognitive load down the line, because I'd end up in much fewer situations where I'd have to figure out "who should free this, and when?".
That's when I realised this cognitive load of keeping the borrow-checker happy was always there in C too. It was just more diffuse, and invisible until it blew up in my face. And when it did it was in the form in nasty runtime problems, not a helpful compiler error that happened before the code was even checked in.