withoutboats / fehler Goto Github PK
View Code? Open in Web Editor NEWRust doesn't have exceptions
License: Other
Rust doesn't have exceptions
License: Other
It would be convenient if a simple use path::to::Error;
were able to override the choice of default error type for a module. Crates sometimes want to use something like anyhow::Error throughout, but can have a module or two where a more specialized type makes more sense. Specifying in an attribute throws(std::io::Error)
to every function in a module is annoying.
pub struct Error;
pub mod m {
#[throws(_)]
pub fn f() {
throw!(crate::Error);
}
}
pub mod n {
use some::other::Error;
#[throws(_)]
pub fn f() {
throw!(some::other::Error);
}
}
Another oddity I'm afraid. This:
use fehler::throws;
#[throws(std::io::Error)] // ERROR: Wrapper type must be a normal path type
pub fn zonk() {
let mut _unused : Box<dyn FnOnce()> = Box::new(||());
}
pub fn works0() -> Result<(), std::io::Error> {
let mut _unused : Box<dyn FnOnce()> = Box::new(||());
Ok(())
}
pub fn works1() {
let mut _unused : Box<dyn FnOnce()> = Box::new(||());
}
#[throws(std::io::Error)]
pub fn works2() {
let mut _unused : Box<()> = Box::new(());
}
Produces this:
error: custom attribute panicked
--> src/lib.rs:4:1
|
3 | #[throws(std::io::Error)] // ERROR: Wrapper type must be a normal path type
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: message: Wrapper type must be a normal path type
I think zonk
should compile and be equivalent to works0
. Again, this test case is minimised from a larger program. Thanks for your attention.
As a person who uses Rust in production, it was confusing at first to see an error!
macro defined by fehler
. I understand that this can easily be handled in Rust 2018 by simply renaming imports:
use log::error as log_error;
// ...or, alternatively:
use fehler::error as throw_err;
...but it'd be nice to remove this papercut altogether and not force people to pick a solution. I'm not partial to what the symbol name ends up being -- just wanted to point out the issue!
If I throw an exception in a function what happens in the calling function? If it's not a throwing function it will receive a Result but if it is, what happens? Receiving a Result in that case may make exceptions much less powerful.
Codes like
#[throws(NotFoundError)]
fn search() {
for item in items {
if item.is_good() { items_found.add(item); return; }
}
throw!("Item")
}
will cause a warning like
warning: unreachable expression
--> src\item_lookup.rs:45:1
|
45 | #[throws(NotFoundError)]
| ^^^^^^^^^^^^^^^^^^^^^
| |
| unreachable expression
| any code following this expression is unreachable
|
= note: `#[warn(unreachable_code)]` on by default
= note: this warning originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
This is because there is an extra Ok
return after the throw
. This can only happen when the return type is ()
.
This code pattern would be very common.
My solution:
#[throws(NotFoundError)]
fn search() {
for item in items {
if item.is_good() { items_found.add(item); return; }
}
// Does not work:
// #[allow(unreachable_code)] throw!("Item") --> still warning, attribute get removed in expansion
// ---
// return throw!("Item"); --> still warning
#[allow(unreachable_code)]
return throw!("Item");
}
Hi!
Many projects turn warnings into errors, which can make using the latest fehler release (necessary if you want to publish on crates.io) a bit unergonomic, as seen in #53, #52 and #42. From what I read in the issues, is the concern that this would be a breaking change? It would be nice to get a new release which includes these fixes.
Thanks!
#[fehler::throws]
fn main() {
potentially_failing_operation()?;
loop {
potentially_failing_operation()?;
}
}
Results in a warning
--> src/main.rs:5:1
|
5 | #[fehler::throws]
| ^^^^^^^^^^^^^^^^^ unreachable expression
...
8 | / loop {
9 | | potentially_failing_operation()?;
10 | | }
| |_____- any code following this expression is unreachable
Expected to be no warning.
I'll try to dig into the source to see if this is possible/trivial to fix.
Script for easy reproduction
cargo new fehler-unreachable
cd fehler-unreachable
echo 'fehler = "*"' >> Cargo.toml
echo '
#[derive(Debug)]
struct Error;
#[fehler::throws]
fn main() {
loop {
}
}
' > src/main.rs
cargo check
P.S.
Thanks for the awesome crate, really cleans up the code!
Can I have the old beautiful shorthand back?
Hi. Thanks for this nice crate. I have only been using it a few hours and it is already improving my life. I have found a situation where I can write something with an obvious meaning but which doesn't do what I would have hoped:
This function
#[must_use] #[throws(X)] fn x() -> usize { 42 }
can be called, and the value 42 discarded, without any warning. This is probably not what the programmer intended when they wrote #[must_use]
.
I suspect that fixing this is not going to be simple. When considering Rust without Fehler, there does not appear to be a way to write a function that returns a Result<T,E> where the caller is required to both handle E and do something with T. (Other than making the whole type T must_use, which may not be desirable or possible.)
My full test program:
use fehler::throws;
use thiserror::Error;
#[derive(Error,Debug)]
#[error("X")]
struct X ();
#[must_use] #[throws(X)] fn x() -> usize { 42 }
#[must_use] fn y() -> Result<usize,X> { Ok(43) }
#[must_use] fn z() -> usize { 44 }
#[throws(X)]
fn main() {
x()?;
y()?;
{ #![allow(unused_must_use)] z(); }
}
Der Fehler is intentionally limited to a predefined set of APIs that I picked when I started working on it, and I'm not interested in adding more to it - such as APIs to enable other patterns of constructing errors. It's also very actively "my" library, and I intend to release it as 1.0 soon and after that make only superficial future changes to it.
But a goal of the project is definitely to pull the ecosystem back toward the Error
trait, rather than failure::Fail
. I want other people to write other crates for their own error handling patterns that they like. I want to write the next iteration of this guidance with more than 4 patterns, linking to several different crates. I want this project to encourage experimentation by other people in their own libraries, to find other patterns.
Along similar lines, it's been pointed out in #12 that throw
and throws
in particular could make a lot of sense as an independent no_std
library. And in #4 we've discussed another pattern that I've always liked (an error and errorkind pair), but that doesn't get used because it involves a lot of boilerplate. Maybe before releasing a 1.0 of this library, I should actually be releasing several libraries at the same time, to encourage a precedent that this isn't some all-encompassing solution.
In thinking about this topic, I guess there are both primary and secondary contexts for an error.
I get mixed signals of which role Context
is supposed to fill
error
field is required, making it seem like it is only for Secondary or is Primary but limited to masking other errorsContextError
treats the context
as the payload of an error, as if it was PrimaryOne side effect of this is that the displayed error looks wonky with Context
. For example, I previously looked at failure
for a library I wrote. The custom errors look like
Error: liquid: Expected whole number, found `fractional number`
with:
end=10.5
from: {% for i in (1..max) reversed %}
from: {% if "blue skies" == var %}
with:
var=10
The original error "liquid: Expected whole number, found fractional number
" is the error someone should programmatically act on. I then add two different kinds of contexts as we unwind the stack, "with"s for data inside a stack frame and "from" for a stack from location.
When considering this for failure
, I would have gotten
Error: liquid: Expected whole number, found `fractional number`
cause: end=10.5
cause: {% for i in (1..max) reversed %}
cause: var=10
cause: {% if "blue skies" == var %}
(might have this inverted; can't remember)
Related to this is a point I raised in https://github.com/mgattozzi/terminator/issues/3 of whether we should treat the cause
/ source
as user-visible context or internal debug data. If I use source
for context and my original error is a std::io::Error
which might not be user-friendly, I need to make choice of displaying it anyways or intentionally dropping it.
Would like to support:
#[throws(as Option)]
etc
The problem is with the throw!
macro. If the try trait were stable, we could instead run this:
return <_ as core::ops::Try>::from_err(..)
But that would make this a nightly only crate.
I'm not sure if
I'd make the syntax work like this:
#[throws(as $wrap)]
fn function() -> $ret
// translated to:
fn function() -> $wrap<$ret>
Whereas:
#[throws($err as $wrap)]
fn function() -> $ret
// translates to:
fn function() -> $wrap<$ret, $err>
This is why I modified the default error type feature to require an explicit _
.
Hello!
Some other error handling crates, like anyhow, have abstractions over std::error::Error (or custom error types in older crates). Does Fehler have something like that, or should I be used std::error::Error myself, or should I be using another crate that wraps around that? By that question, I mean what would seem to cause the least friction in using the library?
Thank you very much and apologies for submitting a question as an issue!
Hi,
How should io::Error
values be converted into fehler::Exception
?
With the recent proliferation of error-handling crates, it has become clear that the situation around the lack of a core::error::Error
is really suboptimal. In snafu
, no_std
support is being introduced through a whole new Error trait just for no_std
- which could lead to similar problems that failure
had by becoming incompatible with the ecosystem.
Ideally, the Error
trait would show up in core, but due to coherence concerns and std-dependent features being added to std::error::Error
, a resolution is unlikely to happen soon. As such, I propose making a new crate, core-error
- exposing our own version of the Error
trait. The goal of this crate is twofolds:
Such a crate would work like this:
std::error::*
no-default-features
, it exposes an Error
trait similar to the one in std
but without backtraces, and without the std/alloc-only impls.The trait will be compatible with std's Error trait, and if libcore gains an Error trait in the future, it should be compatible with it too.
Once the crate reaches 1.0.0, I'll consider it ready for integration in the various error crates and will follow the same stability guarantee Rust does: No breaking changes ever.
Work has already started in https://github.com/core-error/core-error. I'd be interested in hearing feedback on the design.
Would be nice to have actually good documentation here
use anyhow::Error;
use fehler::throws;
#[throws]
fn f() {}
fn g() {}
fn main() {}
warning: function is never used: `g`
--> src/main.rs:7:4
|
7 | fn g() {}
| ^
|
= note: `#[warn(dead_code)]` on by default
It seems throws
functions aren't seen by the compiler as dead code when they should be. Probably some Span is wrong somewhere.
(Tested as of master @ df2e3d4.)
Considering renaming this library to exceptional
. The problem with fehler
is of course how close the pronunciation is to failure
. Also, the name fehler
was based on this being a "successor" to failure
, but the crate that is a successor to failure
in my opinion is really anyhow
.
Yes, it would be nice and helpful to have more examples, especially for newbies like me.
Example code:
use fehler::throws;
use std::io::Error;
fn no_warning() -> Result<(), Error> {
loop {
return Ok(());
}
}
#[throws]
fn unexpected_warning() {
loop {
return;
}
}
fn main() {
no_warning().unwrap();
unexpected_warning().unwrap();
}
Gives this warning:
warning: unreachable expression
--> src/main.rs:10:1
|
10 | #[throws]
| ^^^^^^^^^ unreachable expression
11 | fn unexpected_warning() {
12 | / loop {
13 | | return;
14 | | }
| |_____- any code following this expression is unreachable
|
= note: `#[warn(unreachable_code)]` on by default
= note: this warning originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
$ rustc --version
rustc 1.43.0 (4fb7144ed 2020-04-20)
I have this method
#[throws(i32)]
fn literal(&mut self) -> Literal<'a> {
match self.peek() {
Some(Token {
kind: lit @ (TokenKind::Num(_) | TokenKind::Bool(_) | TokenKind::Unit),
range,
}) => Literal {
pos: range.clone(),
kind: match lit {
TokenKind::Num(n) => LiteralKind::Num(*n),
TokenKind::Bool(b) => LiteralKind::Bool(*b),
TokenKind::Unit => LiteralKind::Unit,
_ => unreachable!(),
},
},
_ => throw!(5),
}
}
and i get, when i try to compile my code:
error: custom attribute panicked
--> src\parser\parser.rs:18:5
|
18 | #[throws(i32)]
| ^^^^^^^^^^^^^^
|
= help: message: #[throws] attribute can only be applied to functions and methods
A way to use Fehler with closures (#[throws] || {...}
) would be nice to have.
The todo!()
macro produces an unreachable expression warning:
#[throws(Error)]
fn foo() {
todo!()
}
warning: unreachable expression
--> rocket_lang/src/main.rs:4:1
|
4 | #[throws(Error)]
| ^^^^^^^^^^^^^^^^ unreachable expression
5 | fn foo() {
6 | todo!()
| ------- any code following this expression is unreachable
|
= note: `#[warn(unreachable_code)]` on by default
= note: this warning originates in the attribute macro `throws` (in Nightly builds, run with -Z macro-backtrace for more info)
It'd be nice if this were be fixed if possible.
I might be doing something wrong here but I had an async trait function:
#[tonic::async_trait]
impl EventStore for EventStoreService {
#[throws(Status)]
async fn emit(
&self,
request: Request<EventRequest>,
) -> Response<EventResponse> {
Response::new(EventResponse { data: "hello world".into() })
}
}
and the compiler throws up on me
error[E0053]: method `emit` has an incompatible type for trait
--> src/grpc/server.rs:27:5
|
27 | #[throws(Status)]
| ^^^^^^^^^^^^^^^^^ expected struct `std::pin::Pin`, found enum `std::result::Result`
|
::: /Users/me/project/target/debug/build/project-4065396c784b1521/out/event_store.rs:72:5
|
72 | #[async_trait]
| -------------- type in trait
|
= note: expected fn pointer `fn(&'life0 grpc::server::EventStoreService, tonic::request::Request<_>) -> std::pin::Pin<std::boxed::Box<(dyn core::future::future::Future<Output = std::result::Result<tonic::response::Response<grpc::server::event_store::EventResponse>, tonic::status::Status>> + std::marker::Send + 'async_trait)>>`
found fn pointer `fn(&'life0 grpc::server::EventStoreService, tonic::request::Request<_>) -> std::result::Result<std::pin::Pin<std::boxed::Box<(dyn core::future::future::Future<Output = tonic::response::Response<grpc::server::event_store::EventResponse>> + std::marker::Send + 'async_trait)>>, tonic::status::Status>`
error[E0271]: type mismatch resolving `<std::result::Result<std::pin::Pin<std::boxed::Box<dyn core::future::future::Future<Output = tonic::response::Response<grpc::server::event_store::EventResponse>> + std::marker::Send>>, tonic::status::Status> as fehler::__internal::_Succeed>::Ok == std::pin::Pin<std::boxed::Box<impl core::future::future::Future>>`
--> src/grpc/server.rs:27:5
|
27 | #[throws(Status)]
| ^^^^^^^^^^^^^^^^^ expected trait `core::future::future::Future`, found opaque type
|
= note: expected type `std::pin::Pin<std::boxed::Box<dyn core::future::future::Future<Output = tonic::response::Response<grpc::server::event_store::EventResponse>> + std::marker::Send>>`
found struct `std::pin::Pin<std::boxed::Box<impl core::future::future::Future>>`
This might have to do with the fact that the expected type is a Pin
?
This compiles no problem:
#[tonic::async_trait]
impl EventStore for EventStoreService {
async fn emit(
&self,
request: Request<EventRequest>,
) -> Result<Response<EventResponse>, Status> {
Ok(Response::new(EventResponse { data: "hello world".into() }))
}
}
Feel free to respond with the old "good luck" and close the issue if this is specific to tonic
.
Side note:
Found this crate in your last couple of blog posts, keep fighting the good fight, it'll be worth it for all of us in the end!
The fehler::Context::context impl on Result looks like a performance hazard for context that is potentially expensive to construct.
use fehler::{throws, Context};
use std::path::Path;
#[throws]
fn f(_path: &Path) {}
fn main() {
let path = Path::new("/");
let _ = f(path).context(format!("failed to f {}", path.display()));
}
Would you consider something like f(path).with_context(|| ...)
or f(path).map_err(|ex| ex.context(...))
?
I believe the following should compile. It does if you replace throw!
with a return or panic.
use fehler::{error, throw, throws};
#[throws]
fn f(b: bool) {
let x;
if b {
x = 0;
} else {
throw!(error!("it fehled"));
}
dbg!(x);
}
As of 1.0.0-alpha.0 it fails with:
error[E0381]: use of possibly-uninitialized variable: `x`
--> src/main.rs:11:10
|
11 | dbg!(x);
| ^ use of possibly-uninitialized `x`
I think it's fair to say that fehler
has been a success. Apparently (#66) aren't able to work on this right now, which is sad.
The way that cargo works means that it is quite disruptive to change the crate name. (It's particularly annoying for downstreams like Debian who curate and do release management.)
Would you consider granting co-ownership of the crate name fehler
(and fehler-macros
) on crates.io to @Nemo157? (I'm also willing to be a crate owner to help reduce bus factor but I don't have tuits right now for reviewing work on fehler.)
Are there any plans for adding a catch!
macro that would allow doing something like
let result: Result<i64> = catch!{
let y = f1()?;
let z = f2()?;
if cond(z) {
throw!(SomeError);
}
y + z
};
The simplest way to do this would be to add support for closures to the throws
macro, and add a catch!
macro like:
macro_rules! catch {
($($tts:tt)*) => { (#[throws(_)] || {$($tts)*})() };
}
But it isn't perfect, since a return inside the catch would behave in an unexpected way (return from the hidden closure instead of the outer function) and lifetimes might cause issues as well.
A better solution would be to make catch!
a procedural macro that wraps the body in labeled scope, and transforms any throw!
or ?
to use a labeled break with value instead of a return. And wraps the final value of the expression. I'm not sure how feasible that is.
Hi. Thanks again for this excellent package.
To reproduce:
#[allow(unused_imports)]
use fehler::throws;
fn parse_args(_f: &dyn Fn(&mut ())) { }
#[throws(std::io::Error)]
fn main()
{
#[derive(Default,Debug)]
struct Args { }
let args = parse_args(&|ma|{ });
let spec = 42;
}
And run cargo build, with fehler="1"
in your dependencies
.
Actual output:
warning: unused variable: `ma`
|
= note: `#[warn(unused_variables)]` on by default
warning: unused variable: `args`
warning: unused variable: `spec`
warning: 3 warnings emitted
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Note the very unhelpful error messages, with no location information and no suggestions.
Expected output:
warning: unused variable: `ma`
--> src/bin/report.rs:11:29
|
11 | let args = parse_args(&|ma|{ });
| ^^ help: if this is intentional, prefix it with an underscore: `_ma`
|
= note: `#[warn(unused_variables)]` on by default
warning: unused variable: `args`
--> src/bin/report.rs:11:9
|
11 | let args = parse_args(&|ma|{ });
| ^^^^ help: if this is intentional, prefix it with an underscore: `_args`
warning: unused variable: `spec`
--> src/bin/report.rs:12:9
|
12 | let spec = 42;
| ^^^^ help: if this is intentional, prefix it with an underscore: `_spec`
warning: 3 warnings emitted
Finished dev [unoptimized + debuginfo] target(s) in 0.17s
This rather odd test case was minimised from a more complicated program. Empirically, the issue goes away if I remove any of (a) #[throws]
(b) the #[derive]
on the struct (c) the closure. The problem can occur with errors as well as warnings. In one particular version of my actual program I got the very unhelpful output type annotations needed
without any indication of where!
Thanks for your attention.
use other_lib::fehler::{self, *}
#[throws]
fn main() {
}
error[E0433]: failed to resolve: could not find `fehler` in `{{root}}`
--> super_app/src/main.rs:5:5
|
5 | #[throws]
| ^^^^^^^^^ could not find `fehler` in `{{root}}`
|
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
when it's exported from another crate other_lib
like:
pub use fehler;
Is it a bug or a feature?
Not really sure about the details, but maybe it makes sense to support multiple different error types and somehow "sum" them together into the Exception type.
Hi! I don't seem to see any license mentioned in the repo? It might make it hard for people to know if they can use this library in their projects...
I'm getting a warning to remove a trailing semicolon in a trait definition, but removing it causes an error:
pub(crate) trait CommandExt {
#[throws]
fn out(&mut self) -> String;
#[throws]
fn status_into_result(&mut self); // <-- this semicolon triggers the warning
}
warning: unnecessary trailing semicolon
--> bin/gen/src/command_ext.rs:8:35
|
8 | fn status_into_result(&mut self);
| ^ help: remove this semicolon
|
When I build my project under alpine linux I get this message:
error: cannot produce proc-macro for `fehler-macros v1.0.0` as the target `x86_64-unknown-linux-musl` does not support these crate types
Please suggest if there is any workaround.
Thank you.
I really liked Exception
. Why was it removed in #26? Was it moved to another create? If not can I at least have it behind a feature flag if the goal is to make it more lightweight?
There also seems to be a new crate because of this change called fehler_more. Which just exports everything from the old version of the fehler crate. That's obviously terrible but proves that other people want to have Exception
, etc. as well.
The immediate fix would be to simply stick with fehler = "1.0.0-alpha.1"
Example code:
use fehler::throws;
use std::io::Error;
#[throws]
fn my_function() -> Vec<u8> {
type Foo = fn(usize);
std::fs::read("blahblah")?
}
fn main() {
my_function().unwrap();
}
Compilation fails with:
error: custom attribute panicked
--> src/main.rs:4:1
|
4 | #[throws]
| ^^^^^^^^^
|
= help: message: Wrapper type must be a normal path type
$ rustc --version
rustc 1.43.0 (4fb7144ed 2020-04-20)
Can der Fehler, in principle (I see it's not implemented right now), work with #![no_std]
and without alloc
?
It would be great to get a version published that includes 0bde837.
This code:
trait Example {
#[throws]
fn foo();
}
expands to this:
trait Example {
fn foo() -> ::core::result::Result<(), Error> {
<_ as ::fehler::__internal::_Succeed>::from_ok(())
}
}
This inserts a default method for the trait which has two annoying effects:
This is using rust 1.42.0 with fehler 1.0.0.
The problem appears to be in Throws::fold
because the trait method is parsed by parse
for ImplItemMethod
. The implementation of this function in syn includes this comment:
// Accept methods without a body in an impl block because
// rustc's *parser* does not reject them (the compilation error
// is emitted later than parsing) and it can be useful for macro
// DSLs.
which causes this problem.
When an exception is propagated all the way to the top-level of the crate, it's useful to print the list of causes. For failure
this behavior exists in exitfailure
, but it'd be nice if fehler::Exception
would support this too.
I believe this can be implementing using the std::process:Termination
trait.
impl std::process::Termination for Exception {
fn report(self) -> i32 {
for err in self.errors() {
eprintln!("{}", err);
}
1
}
}
Is there anything we should do about backtraces? Something akin to colorize-backtrace
for debug builds perhaps? It may also just be out of scope for this crate (as it mostly seems to emphasize core functionality).
Implementing Display is actually the most annoying part of defining your own error. Should der Fehler provide a derive to help you with this?
One option is one of the relatively "obvious" less opinionated derives, like the one found in the display derive crate I wrote for failure. Another option is to do something fancy like deriving the display from doc comments.
Code:
use fehler::throws;
trait FooTrait {}
struct FooStruct;
struct FooError;
impl FooTrait for FooStruct {}
#[throws(FooError)]
fn foo() -> Box<dyn FooTrait> {
Box::new(FooStruct)
}
Compile result:
Error[E0271]: type mismatch resolving ...
|
104 | #[throws(FooError)]
| ^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn mymodule::FooTrait`, found struct `mymodule::FooStruct`
|
= note: expected type `std::boxed::Box<dyn mymodule::FooTrait>`
found struct `std::boxed::Box<mymodule::FooStruct>`
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
I've used fehler on a few small projects and like it very much. (Thank you!)
I'm considering using it for a larger project, but I'm worried that having the #[throws]
macros on a bunch of functions in a large codebase may increase compile times, just from having all functions be filtered through a proc-macro invocation.
Is this something I should be worried about?
#![feature(stmt_expr_attributes, proc_macro_hygiene)]
type Error = usize;
fn main() {
let x = #[fehler::throws] || 5;
dbg!(x());
}
I expected this to work and print Ok(5)
, with an expansion something like
let x = || Result<_, Error> { Ok({ 5 }) };
#[throws(Error)]
fn foo(a: Option<u8>) -> u8 {
let Some(a) = a else {
return 0;
};
a
}
does not work.
You need to do
#[throws(Error)]
fn foo(a: Option<u8>) -> u8 {
let Some(a) = a else {
return Ok(0);
};
a
}
to make it compile.
In contrast, there is no need for this Ok(...)
if you use if let - else
:
#[throws(Error)]
fn foo(a: Option<u8>) -> u8 {
if let Some(a) = a {
a
} else {
return 0;
}
}
But I mean, let-else breaks even rustfmt (I had to manually format the code) and github syntax highlighting (else
in the first code snippet does not get highlighted as a keyword, return
does not get highlighted in the first two), so yeah, a breakage in a 2yo procmacro is not really surprising. (Wonder if using a separate keyword like guard
would be a better decision or having to bump the edition again would be to much of a hustle.)
Anyways, nice article (and the whole blog too):
https://without.boats/blog/why-ok-wrapping/
Btw, do you happen to know if any progress has been made on this feature in rust proper over the last two years since the blogpost? I, personally, would love to see this in Rust, but from what I can tell, it's a bit controversial with some people and definitely does not seem to be a high priority for the language right now.
Thanks for this crate! Minor annoyance I stumbled upon:
Given a function:
#[throws(())]
fn always_throw() {
throw!(());
}
A compile warning pops out:
| #[throws(())]
| ^^^^^^^^^^^^^
| |
| unreachable expression
| any code following this expression is unreachable
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.