Here is a simple testcase, which still works on the playpen:
use std::num;
use std::str::FromStr;
use std::convert::From;
#[derive(Debug)]
struct Error(String);
impl From<num::ParseFloatError> for Error {
fn from(err: num::ParseFloatError) -> Error {
Error(format!("{}", err))
}
}
fn parse(s: &String) -> Result<f64, Error> {
Ok(try!(<f64 as FromStr>::from_str(&s[..])))
}
fn main() {
println!("{:?}", parse(&"10.01".to_string()));
}
However, after I built the latest rustc from git (now it's rustc 1.1.0-dev (1114fcd94 2015-04-23)), it stopped compiling with following error:
<std macros>:6:1: 6:32 error: the trait `core::convert::From<core::num::ParseFloatError>` is not implemented for the type `Error` [E0277]
<std macros>:6 $ crate:: convert:: From:: from ( err ) ) } } )
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<std macros>:1:1: 6:48 note: in expansion of try!
exp.rs:15:8: 15:48 note: expansion site
error: aborting due to previous error
I'm unable to find out what's wrong. Why is the compiler unable to find my trait implementation?
This looks like it is a bug: std::num::ParseFloatError and <f64 as FromStr>::Err are different types:
the impl of FromStr for f64 is in core, and hence uses a ParseFloatError type defined in that crate, so any uses of FromStr/.parse() will get this type.
std::num defines a new ParseFloatError type, so an import from std::num gets this one.
The impl From<num::ParseFloatError> for Error is using the latter, while <f64 as FromStr>::from_str(...) is returning the former.
I opened #24748 about it. I also opened #24747 about improving the diagnostics to make this easier to debug in future.
One can work around this by insteading implementing the trait for core::num::ParseFloatError. You'll need to load the core crate with extern crate core; and will need some feature gates.
Related
Reproduction project (single main.rs file): https://github.com/frederikhors/iss-custom-err.
I'm trying to create a custom error for my app:
pub struct AppError {
message: String,
error: anyhow::Error, // In the future I would also avoid anyhow
}
I'm trying to use it in my code but as you can see I'm getting the below compiler errors, why?
Isn't my AppError implementing the trait std::error::Error correctly?
I would expect an auto conversion from hyper error to AppError being both error:Error traits, am I wrong?
error[E0277]: `?` couldn't convert the error to `AppError`
--> src\main.rs:20:44
|
20 | .body(Body::from(body))?;
| ^ the trait `From<hyper::http::Error>` is not implemented for `AppError`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following other types implement trait `FromResidual<R>`:
<Result<T, F> as FromResidual<Result<Infallible, E>>>
<Result<T, F> as FromResidual<Yeet<E>>>
= note: required because of the requirements on the impl of `FromResidual<Result<Infallible, hyper::http::Error>>` for `Result<(), AppError>`
error[E0277]: `?` couldn't convert the error to `AppError`
--> src\main.rs:24:19
|
24 | .await?;
| ^ the trait `From<hyper::Error>` is not implemented for `AppError`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following other types implement trait `FromResidual<R>`:
<Result<T, F> as FromResidual<Result<Infallible, E>>>
<Result<T, F> as FromResidual<Yeet<E>>>
= note: required because of the requirements on the impl of `FromResidual<Result<Infallible, hyper::Error>>` for `Result<(), AppError>`
For more information about this error, try `rustc --explain E0277`.
I would expect an auto conversion from hyper error to AppError being both error:Error traits, am I wrong?
Yes. Error is a usage trait, it's like IntoIterator: both Vec and HashMap implement it, but that doesn't mean you can convert an arbitrary Vec to a HashMap.
Let alone that Rust will do it for you: Rust generally favors intentionality, it avoids large multi-use concepts. So all error tells you is that you can display the type, and may be able to get its source. It says nothing about conversion, except for conversion to a Box<dyn Error> (because Error is object-safe).
So as the compiler error tells you, if you want Rust (and specifically ?) to perform the conversion, you need to implement the From trait.
Note: libraries like thiserror or snafu provide tooling to more easily create bespoke error types, and conversions from existing error types.
On discord a kind user helped me understand.
What I need is a generic impl:
impl<T: error::Error + Send + Sync + 'static> From<T> for AppError {
fn from(e: T) -> Self {
Self { message: e.to_string(), error: anyhow::Error::new(e) }
}
}
That's it!
In the code below: Rust Playground
//////////////////////////////////////
// External code from another crate //
//////////////////////////////////////
trait FooExternal {
fn foo(&self);
}
fn foo_external(f: impl FooExternal) {
f.foo();
}
/////////////////////////////////////
// My generated library code below //
/////////////////////////////////////
trait FooImpl {
fn foo_impl(&self);
}
impl<T: FooImpl> FooExternal for T {
fn foo(&self) {
println!("foo: boilerplate");
self.foo_impl();
}
}
////////////////////////
// My user code below //
////////////////////////
#[derive(Debug, Default)]
struct Foo;
// NB: the compiler will yell if FooImpl is not implemented
// try commenting out the impl below
impl FooImpl for Foo {
fn foo_impl(&self) {
println!("foo_impl");
}
}
fn main() {
println!("Hello, world!");
let f = Foo::default();
foo_external(f);
}
The external code expects user to supply a type that implements FooExternal, so foo_external can work on an instance of the type.
However, when implementing the FooExternal trait for a user defined struct Foo, there is some boilerplate code (e.g. setting up opentelemetry tracing span context for each tonic grpc handler) that is common and error prone and a little complex to require an end user to type out.
So I plan to generate (think: proc macro, but codegen is not the issue here!) the common boilerplate implementation of FooExternal, and want user to only focus on the core app logic, and not worrying about the complex and boring chore of typing the same boilerplate over and over again!
So, instead of having user to implement FooExternal for her type Foo, I want the user to implement a generated trait FooImpl, where in the generated blanket trait implementation of FooExternal::foo, the boilerplate code is emitted, and the control is forwarded to FooImpl::foo_impl.
Here's the nice thing in the user code: FooImpl becomes a requirement for the user type Foo (when using foo_external) - if the user forgets to implement FooImpl for Foo, the compiler would kindly yell at you when calling foo_external(f)!
So it seems that the trait blanket implementation effectively have FooExternal bound on FooImpl - even the compiler error message (when the impl FooImpl for Foo is commented out) says:
Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `Foo: FooImpl` is not satisfied
--> src/main.rs:49:18
|
49 | foo_external(f);
| ^ the trait `FooImpl` is not implemented for `Foo`
|
note: required because of the requirements on the impl of `FooExternal` for `Foo`
--> src/main.rs:23:18
|
23 | impl<T: FooImpl> FooExternal for T {
| ^^^^^^^^^^^ ^
note: required by a bound in `foo_external`
--> src/main.rs:11:25
|
11 | fn foo_external(f: impl FooExternal) {
| ^^^^^^^^^^^ required by this bound in `foo_external`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to previous error
So, I'm wondering that is trait blanket implementation ever designed to be used in such a scenario (basically tie FooImpl and FooExternal together, a little like extension trait trait FooExternal: FooImpl, but not quite!), and can I rely on this behavior to define my code generation logic to generate the library code as in the code sample?
Any insights would be greatly appreciated!
According to the trait coherence rules, impl<T: FooImpl> FooExternal for T must be in the same crate as FooExternal. So, if you do not control the crate FooExternal is defined in, you cannot do this.
The rules are are designed so that adding more crates can never create a conflict between trait implementations that wasn't there already. There can only be one blanket implementation impl<T...> FooExternal for T (because if there was more than one, they might both apply to the same type T), and only the crate defining FooExternal is allowed to write that implementation.
I have a RwLock protected global WORLD, and I want to write a function that read locks it and returns an iterator (of type Neighbors) that iterates over edges in a petgraph::stable_graph::StableGraph that is stored inside the global. I'm using OwningRef to deal with keeping the read lock guard alive after the function exits, which has worked for me in the past when just returning a field of World directly. I've included a compilable example and the error I'm getting below -- it seems there is some sort of type problem but I haven't been able to figure it out. I think it might have to do with OwningRef wanting to deal with a reference rather than an object containing a reference (Neighbors) but I'm not sure how to work around that.
Cargo.toml:
[package]
name = "problem_demo"
version = "0.1.0"
authors = ["Joseph Garvin <joseph.h.garvin#gmail.com>"]
edition = "2018"
[dependencies]
owning_ref="0.4.1"
once_cell="1.4.0"
petgraph={version="0.5.1", features=["serde-1"]}
main.rs:
use std::{sync::RwLock};
use once_cell::sync::OnceCell;
use owning_ref::RwLockReadGuardRef;
use petgraph::stable_graph::{StableGraph, Neighbors};
struct Bar {
data: i32
}
struct World {
graph: StableGraph<(), Bar, petgraph::Directed, u32>
}
pub static WORLD: OnceCell<RwLock<World>> = OnceCell::new();
fn neighbors(id: u32) -> Result<RwLockReadGuardRef<'static, World, Neighbors<'static, Bar, u32>>, Box<dyn std::error::Error>> {
RwLockReadGuardRef::new(WORLD.get().unwrap().read().unwrap())
.try_map(
|world: &World| -> std::result::Result<Neighbors<'static, Bar, u32>, Box<dyn std::error::Error>>
{
let neighbors = world.graph.neighbors_directed(
petgraph::graph::NodeIndex::new(id as usize),
petgraph::Direction::Outgoing
);
Ok(neighbors)
}
)
}
Errors:
error[E0271]: type mismatch resolving `for<'r> <[closure#src/main.rs:21:13: 29:14 id:_] as std::ops::FnOnce<(&'r World,)>>::Output == std::result::Result<&'r _, _>`
--> src/main.rs:20:10
|
20 | .try_map(
| ^^^^^^^ expected struct `petgraph::stable_graph::Neighbors`, found reference
|
= note: expected enum `std::result::Result<petgraph::stable_graph::Neighbors<'static, Bar>, std::boxed::Box<dyn std::error::Error>>`
found enum `std::result::Result<&_, _>`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0271`.
error: could not compile `playground`.
To learn more, run the command again with --verbose.
I am attempting to set up a project very similar to dueboot. That is, Rust on embedded ARM. Right now, I'm only up to the point of compiling the Rust code, but I can't get it to compile.
I've basically copied the rust code exactly from that project, but I don't fully understand the lang_items feature.
#![feature(asm)]
#![feature(lang_items)]
#![feature(no_std)]
#![no_std]
use arduino::{init, delay, pinMode, digitalWrite, analogWrite, LOW, HIGH, OUTPUT};
mod arduino;
#[lang="sized"]
trait Sized {}
#[lang="copy"]
trait Copy {}
#[lang="sync"]
trait Sync {}
static PWM:u32 = 2;
static LED:u32 = 11;
#[no_mangle]
pub fn main() {
// ...
}
Attempting to compile the code as above, results in this error:
main.rs:11:1: 11:15 error: parameter `Self` is never used
main.rs:11 trait Sized {}
^~~~~~~~~~~~~~
main.rs:14:1: 14:14 error: parameter `Self` is never used
main.rs:14 trait Copy {}
^~~~~~~~~~~~~
main.rs:17:1: 17:14 error: parameter `Self` is never used
main.rs:17 trait Sync {}
^~~~~~~~~~~~~
error: aborting due to 3 previous errors
I also attempted to comment out all of the lines relating to the lang_items, including the feature line at the top. This is the resulting error message:
// more lines of the same message
error: requires `sized` lang_item
error: requires `sized` lang_item
error: requires `sized` lang_item
error: requires `sized` lang_item
error: requires `sized` lang_item
error: aborting due to 54 previous errors
I'm using rust built from master, as of yesterday.
Any suggestions?
A few days ago, Rust added the rule that all type parameters must appear in the methods of the trait (RFC 738). But since Sized (and Copy and Sync) doesn't have any methods of its own, it breaks this rule by default.
The official workaround is to mark the trait as PhantomFn, which silences the error:
#![feature(asm)]
#![feature(lang_items)]
#![feature(no_std)]
#![no_std]
use arduino::{init, delay, pinMode, digitalWrite, analogWrite, LOW, HIGH, OUTPUT};
mod arduino;
// Copied from <http://doc.rust-lang.org/core/marker/trait.PhantomFn.html>
#[lang="phantom_fn"]
trait PhantomFn<A: ?Sized, R: ?Sized = ()> {}
#[lang="sized"]
trait Sized: PhantomFn<Self> {}
#[lang="copy"]
trait Copy: PhantomFn<Self> {}
#[lang="sync"]
trait Sync: PhantomFn<Self> {}
static PWM:u32 = 2;
static LED:u32 = 11;
#[no_mangle]
pub fn main() {
// ...
}
This change should make the code compile again.
Addendum: What are lang items?
A lang item is a symbol that's marked as "special" to the compiler. Some examples are:
The heap allocator
Procedures for unwinding on panic
Types and traits that ensure type safety, like Send and Drop and PhantomData
Traits for operator overloading, like Add and Eq and Deref
You can find a list of them in lang_items.rs.
These items are critical to the semantics of the language, but are impractical to implement in the compiler itself. So we put them in the standard library instead, with special annotations that tell the compiler where they are.
Now, while you can define these items itself, it's recommended to link to core instead, which declares these lang items for you. But I'm not sure how well that would work with your setup.
I'm trying to compile the following code, from the Rust book at the Rust official website.
fn takes_slice(slice: &str) {
println!("Got: {}", slice);
}
fn main() {
let s = "Hello".to_string();
takes_slice(&s);
}
At compilation, it throws the following error
/devl/rust/bc_09/src/main.rs:7:17: 7:19 error: mismatched types:
expected &str, found &collections::string::String (expected str,
found struct collections::string::String)
/devl/rust/bc_09/src/main.rs:7 takes_slice(&s);
^~ error: aborting due to previous error Could not compile hello_world.
Here is the Rust version I'm running: rustc 1.0.0-nightly (44a287e6e 2015-01-08 17:03:40 -0800)
That's a really old version of the nightly in Rust terms! Old enough that the &String -> &str coercion isn't available. You just need to upgrade to a newer version.