use once_cell::sync::OnceCell;
pub trait SomeTrait {}
pub struct Impl1 {}
impl SomeTrait for Impl1 {}
pub static GLOBAL_THING: OnceCell<Box<dyn SomeTrait>> = OnceCell::new();
pub fn main() {
GLOBAL_THING.set(Box::new(Impl1 {})).unwrap();
}
I'm getting this error and not sure how to interpret it's meaning or fix it.
error[E0599]: the method `unwrap` exists for enum `Result<(), Box<(dyn SomeTrait + 'static)>>`, but its trait bounds were not satisfied
--> src/main.rs:11:42
|
11 | GLOBAL_THING.set(Box::new(Impl1 {})).unwrap();
| ^^^^^^ method cannot be called on `Result<(), Box<(dyn SomeTrait + 'static)>>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`Box<dyn SomeTrait>: Debug`
An easy workaround is to just do if let Err() = GLOBAL_THING.set() {panic!(...)} instead of using unwrap(), but I'd like to understand what's going on here and fix if possible.
If you take a look at the unwrap method documentation you'll see that it's not defined for all Results, but only ones where E: Debug:
impl<T, E> Result<T, E>
where
E: Debug,
{
pub fn unwrap(self) -> T;
}
It needs the error type E to implement Debug so that the error can be printed if unwrapping fails.
The error message shows that the result type is Result<(), Box<(dyn SomeTrait + 'static)>>, so E = Box<(dyn SomeTrait + 'static)>. You can make this error type debuggable by having SomeTrait: Debug, which requires that any type that implements SomeTrait must also implement Debug.
pub trait SomeTrait: Debug {}
#[derive(Debug)]
pub struct Impl1 {}
Once you do this you'll hit the next error:
error[E0277]: `dyn SomeTrait` cannot be shared between threads safely
--> src/main.rs:11:1
|
11 | pub static GLOBAL_THING: OnceCell<Box<dyn SomeTrait>> = OnceCell::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `dyn SomeTrait` cannot be shared between threads safely
|
= help: the trait `Sync` is not implemented for `dyn SomeTrait`
= note: required because of the requirements on the impl of `Sync` for `Unique<dyn SomeTrait>`
= note: required because it appears within the type `Box<dyn SomeTrait>`
= note: required because of the requirements on the impl of `Sync` for `once_cell::imp::OnceCell<Box<dyn SomeTrait>>`
= note: required because it appears within the type `once_cell::sync::OnceCell<Box<dyn SomeTrait>>`
= note: shared static variables must have a type that implements `Sync`
To fix this you'll also want to make the boxed trait object Send + Sync so that it can be shared across threads.
pub static GLOBAL_THING: OnceCell<Box<dyn SomeTrait + Send + Sync>> = OnceCell::new();
You can see the final result on the Playground.
Related
I am struggling with code to write containers to file. I have specified a trait bound of std::fmt::Display because any type implementing this has to_string(). f64 implements this trait, so I don't understand how this error could happen. I checked the rust issues on github and nothing appears to be there but I wanted to check my understanding before raising an issue.
type mismatch resolving `<Vec<f64> as IntoIterator>::Item == (dyn std::fmt::Display + 'static)`
expected trait object `(dyn std::fmt::Display + 'static)`
found type `f64` rustc (E0271)
Edit:
The ToString trait is what I actually tried first, and it gives the same error
type mismatch resolving `<Vec<f64> as IntoIterator>::Item == (dyn ToString + 'static)`
expected trait object `(dyn ToString + 'static)`
found type `f64` rustc (E0271)
Edit:
Added relevant code. I also have an error where the compiler complains about x not being a reference, which I haven't figured out and is why I didn't initially have the code in this question.
use std::error::Error;
// Write to a file
pub fn write_to_file<T>(data_line: T) -> Result<(), Box<dyn Error>>
where
T: IntoIterator<Item = dyn ToString> + len_trait::Len,
{
let file = std::fs::OpenOptions::new()
.write(true)
.create(true)
.append(true)
.open("test_data/data.tsv")
.unwrap();
let mut wtr = csv::WriterBuilder::new()
.delimiter(b'\t')
.quote_style(csv::QuoteStyle::NonNumeric)
.from_writer(file);
let mut data_line_str: Vec<String> = Vec::with_capacity(data_line.len());
// into_iter() is used instead of iter() because iter() doesn't have a trait
// and into_iter() consumes the reference, not the original object
data_line
.into_iter()
.for_each(|x| data_line_str.push(x.to_string()));
wtr.write_record(&data_line_str)?;
wtr.flush()?;
Ok(())
}
// Use assertions to check for problems
#[test]
fn test_write() -> Result<(), Box<dyn Error>>
{
let test: Vec<f64> = vec![1.423, 0.61324, 123.865];
write_to_file(test)?;
// Could use assert_eq! and open file and check matching
let _remove_success = std::fs::remove_file("test_data/data.tsv");
Ok(())
}
Edit:
I've tried changing the function definition, haven't changed to use collect yet because I'm trying to pick apart how this works, and I get this error... I thought I understood but now I'm confused again.
error[E0599]: no method named `len` found for type parameter `T` in the current scope
--> src/io.rs:22:71
|
4 | pub fn write_to_file<T, I>(data_line: T) -> Result<(), Box<dyn Error>>
| - method `len` not found for this type parameter
...
22 | let mut data_line_str: Vec<String> = Vec::with_capacity(data_line.len());
| ^^^ method not found in `T`
|
= help: items from traits can only be used if the type parameter is bounded by the trait
help: the following traits define an item `len`, perhaps you need to restrict type parameter `T` with one of them:
|
6 | T: IntoIterator<IntoIter = I> + ExactSizeIterator,
| +++++++++++++++++++
6 | T: IntoIterator<IntoIter = I> + FixedInitializer,
| ++++++++++++++++++
For more information about this error, try `rustc --explain E0599`.
Final edit:
I believe that I understand the issue. When I bound IntoIterator::IntoIter to ExactSizeIterator, that is specifying what the object that can be turned into an iterator (that is, implements IntoIterator) is turned into an ExactSizeIterator which at that point has a len(). So data_line may not have a len() method, but data_line.into_iter() would when I do as PitaJ suggests and use a type signature like
pub fn write_to_file<T, I>(data_line: T) -> Result<(), Box<dyn Error>>
where
T: IntoIterator<IntoIter = I>,
I: ExactSizeIterator,
<I as Iterator>::Item: ToString,
T: IntoIterator<Item = dyn ToString> + len_trait::Len,
What this says is "I expect T to be able to be converted into an iterator containing some unknown type of trait objects for ToString. That's actually a pretty complicated iterator type, and since dyn ToString is unsized, I'm not entirely confident you can ever have any valid instance of this type.
In most reasonable use cases for this function, we know the type of the iterator at compile time, so you're looking for generics, not trait objects. Consider
pub fn write_to_file<T, I>(data_line: T) -> Result<(), Box<dyn Error>>
where
T: IntoIterator<Item = I> + len_trait::Len,
I: ToString
The following code allows me to downcast from Arc<MyTrait> to Arc<MyStruct>:
trait MyTrait {
fn as_any(self: Arc<Self>) -> Arc<dyn std::any::Any + Send + Sync>;
};
struct MyStruct {};
MyStruct impl MyTrait {
fn as_any(self: std::sync::Arc<Self>) -> std::sync::Arc<dyn std::any::Any + Send + Sync> {
self
}
};
Now I am in the need to downcast from Arc<tokio::sync::RwLock<MyTrait>> to Arc<tokio::sync::RwLock<MyStruct> while keeping the original object alive as it is stored in the list to which other parts of the program have access to?
Adding
fn as_any_rw(self: Arc<tokio::sync::RwLock<Self>>) -> Arc<tokio::sync::RwLock<dyn std::any::Any + Send + Sync>>;
to the trait makes the compiler scream with
invalid `self` parameter type: std::sync::Arc<tokio::sync::RwLock<Self>>
type of `self` must be `Self` or a type that dereferences to it
consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`)rustcE0307
I know that using enums is an alternative approach to solve my problem but I have to work with the existing structure.
I'm trying to create a sort-of observer pattern in rust and I am getting a compile error that is basically telling me (I think) it doesn't know the size of a function param at runtime. I didn't realize the size of a function pointer was unknown at runtime. More importantly, I don't have a clue how to tell it the size
christianb#christianb-mac debug % cargo build
Compiling pyrsia-blockchain v0.1.0 (/Users/christianb/dev/jfrog/rusty-brown)
error[E0277]: the size for values of type `(dyn Fn(Foo) -> Result<(), (dyn std::error::Error + 'static)> + 'static)` cannot be known at compilation time
--> src/main.rs:45:16
|
45 | observers: HashMap<&'a Foo, OnFooDone>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn Fn(Foo) -> Result<(), (dyn std::error::Error + 'static)> + 'static)`
note: required by a bound in `HashMap`
error[E0277]: the size for values of type `(dyn std::error::Error + 'static)` cannot be known at compilation time
--> src/main.rs:45:16
|
45 | observers: HashMap<&'a Foo, OnFooDone>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn std::error::Error + 'static)`
note: required by a bound in `Result`
error[E0277]: the size for values of type `(dyn std::error::Error + 'static)` cannot be known at compilation time
--> src/main.rs:49:54
|
49 | pub fn submit_foo(&mut self, foo: &Foo, on_done: OnFooDone) -> &Self {
| ^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn std::error::Error + 'static)`
note: required by a bound in `Result`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `pyrsia-blockchain` due to 3 previous errors
use std::collections::HashMap;
use std::error::Error;
fn main() {
let mut holder = Holder {
observers: HashMap::new()
};
let foo0 = Foo {
id: 0,
stuff: **"hello",
};
let foo1 = Foo {
id: 1,
stuff: **"world",
};
let mut foo2 = Foo {
id: 2,
stuff: **"bob",
};
let mut magic_num = 5;
let mut closure = |foo| {
println!("Slow");
magic_num += foo.id;
foo.id
};
holder.submit_foo(&foo0, |f| {
println!("received foo {}", f.id)?
});
holder.submit_foo(&foo1, |f| {
println!("received foo2 {}", f.id)?
});
holder.submit_foo(&foo2, closure);
holder.notify_all();
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Foo {
id: u64,
stuff: str,
}
type OnFooDone = dyn Fn(Foo) -> Result<(),dyn Error>;
pub struct Holder<'a> {
observers: HashMap<&'a Foo, OnFooDone>,
}
impl Holder<'_> {
pub fn submit_foo(&mut self, foo: &Foo, on_done: OnFooDone) -> &Self {
self.observers.insert(foo, on_done);
self
}
pub fn notify_all(self) -> Self {
self.observers.iter().for_each(|k, f| f(k));
self
}
}
The problem is with:
type OnFooDone = dyn Fn(Foo) -> Result<(),dyn Error>;
This is a so-called unsized type because its size is not known at compile time. It's not a function pointer.
Let's start with the type itself. Imagine if you have just a regular function, but also a closure. It's pretty obvious now, that the closure is not the same size as the function pointer, but both implement Fn.
The same issue is present with the Result. What if the function returns several different types that implement dyn Error ?
The solution is to either use references or box the dynamically sized types:
You can return a boxed error, such as Box<dyn Error>> instead of just dyn Error
You can either box the Fn, just like we did with error above, or store a reference: HashMap<&'a Foo, &'a OnFooDone>
Thus we get:
type OnFooDone = dyn Fn(Foo) -> Result<(),Box<dyn Error>>;
pub struct Holder<'a> {
observers: HashMap<&'a Foo, &'a OnFooDone>,
// or with a boxed triat
observers: HashMap<&'a Foo, Box<OnFooDone>>,
}
Resources:
Returning closures
This question already has answers here:
How to clone a struct storing a boxed trait object?
(3 answers)
Closed last year.
I am using using clonable futures using the shared() method on a future that returns Result<T, Box<dyn Error>>. However, Clone is not implemented for dyn Error. I tried creating my own custom error type (CustomError) that wraps Box<dyn Error> instead and tried to implement Clone on that but it still doesn't work. I feel like I am missing something trivial here.
Here's what I tried
use std::error::Error;
#[derive(Debug)]
pub struct CustomError(pub Box<dyn Error>);
impl Clone for CustomError{
fn clone(&self) -> Self {
CustomError(Box::*new*(self.0.clone())) // doesn't work due to unsatisfied trait bounds, what else can I do?
}
}
Here's the error I get if I try to use Box<dyn Error> as my return type when calling shared() on the future. Any solutions to this?
error[E0277]: the trait bound `(dyn StdError + 'static): Clone` is not satisfied
--> src/scrapers/form_4_xml_scraper.rs:56:52
|
56 | let document_fut = self.scrape(filing).shared();
| ^^^^^^ the trait `Clone` is not implemented for `(dyn StdError + 'static)`
|
= note: required because of the requirements on the impl of `Clone` for `Box<(dyn StdError + 'static)>`
= note: 1 redundant requirements hidden
= note: required because of the requirements on the impl of `Clone` for `std::result::Result<(Filing, Form4XMLDocument), Box<(dyn StdError + 'static)>>
For anyone else wondering, I used Arc instead of Box and it worked perfectly.
Here's my test code:
use std::error::Error;
use std::fmt;
struct Handler {
error: String
}
#[derive(Debug)]
struct SpecificError;
impl fmt::Display for SpecificError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "SpecificError")
}
}
impl Error for SpecificError {}
impl<E: Error> From<E> for Handler {
fn from(e: E) -> Self {
Handler { error: format!("{}", e) }
}
}
fn fail1() -> Result<(), SpecificError> {
Err(SpecificError)
}
fn fail2() -> Result<(), Box<Error>> {
Err(Box::new(SpecificError))
}
fn handler() -> Result<(), Handler> {
fail1()?;
fail2()?;
Ok(())
}
The call to fail1() is fine, but the call to fail2() doesn't compile:
error[E0277]: the size for values of type `dyn std::error::Error` cannot be known at compilation time
--> src/main.rs:35:5
|
35 | fail2()?;
| ^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `dyn std::error::Error`
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: required because of the requirements on the impl of `std::error::Error` for `std::boxed::Box<dyn std::error::Error>`
= note: required because of the requirements on the impl of `std::convert::From<std::boxed::Box<dyn std::error::Error>>` for `Handler`
= note: required by `std::convert::From::from`
I agree with the compiler that dyn Error doesn't have a size known at compile time, but I don't understand why that's relevant, since the type I'm attempting to convert from is a Box<dyn Error>, which does have a size known at compile time.
TL;DR: I'm pretty sure that you cannot in a generic way.
I don't understand why that's relevant, since the type I'm attempting to convert from is a Box<dyn Error>, which does have a size known at compile time.
That's not the place it's complaining about. Look at the error message again (slightly cleaned up):
the trait `Sized` is not implemented for `dyn Error`
required because of the requirements on the impl of `Error` for `Box<dyn Error>`
required because of the requirements on the impl of `From<Box<dyn Error>>` for `Handler`
required by `From::from`
The second line is the important one. Here's a simpler reproduction of your problem:
use std::error::Error;
fn example<E: Error>() {}
fn main() {
example::<Box<dyn Error>>();
}
error[E0277]: the size for values of type `dyn std::error::Error` cannot be known at compilation time
--> src/main.rs:6:5
|
6 | example::<Box<dyn Error>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `dyn std::error::Error`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: required because of the requirements on the impl of `std::error::Error` for `std::boxed::Box<dyn std::error::Error>`
note: required by `example`
--> src/main.rs:3:1
|
3 | fn example<E: Error>() {}
| ^^^^^^^^^^^^^^^^^^^^^^
Error is only implemented for Box<T> when T is Sized and implements Error itself:
impl<T: Error> Error for Box<T> {
// ...
}
Said another way, Box<dyn Error> does not implement Error.
One might think that you can add a second implementation of From for Box<Error>, but this is disallowed:
upstream crates may add new impl of trait `std::error::Error` for type
`std::boxed::Box<(dyn std::error::Error + 'static)>` in future versions
The best alternative I have to offer is to implement From for each individual concrete type you need to support:
impl From<SpecificError> for Handler {
fn from(e: SpecificError) -> Self {
Handler { error: format!("{}", e) }
}
}
impl From<Box<dyn Error>> for Handler {
fn from(e: Box<dyn Error>) -> Self {
Handler { error: format!("{}", e) }
}
}
A macro can reduce the boilerplate here.