I want T
to be something like Foo<'a>
.
No, it is the vtable, and this is a problem for all trait objects regardless of where they are stored, behind a reference, in a Box
, or in an Arc
let a: Arc<dyn Debug> = Arc::new(10);
let b : Arc<dyn Any> = Arc::new(a) as Arc<dyn Any>;
Here you are coercing from Arc<Arc<dyn Debug>>
to Arc<dyn Any>
. This is a thin-pointer to fat pointer (Arc<SizedType>
to Arc<UnsizedType>
) conversion, which is allowed.
Fat-pointer to fat-pointer coercions (Arc<UnsizedType1>
to Arc<UnsizedType2>
) are not supported. In particular, coercing from Arc<dyn Trait1>
to Arc<dyn Trait2>
would require a way to get the Trait2 vtable pointer from the Trait1 trait object, which is not currently possible.
It would kinda be cool if you could do things like
let error = Box<MyError::new("message")> as Box<dyn Error>;
let displayable = error as Box<dyn Display>;
although this does look suspiciously like OOP.
Coming back to this, I don't think this requires converting dyn Fn
to dyn Any
. The type you want to put in the map is an Arc<Vec<_>>
which is a regular sized type, and you should be able to use map.insert(handlers)
to put it in the map. The following code compiles if I paste it into your lib.rs:
fn foo<T: Event + ?Sized>(handlers: Handlers<T>) {
use anymap::{Map, any::Any};
let mut map: Map<Any + Send + Sync> = Map::new();
map.insert(handlers);
}
Does the following? (imports skipped)
struct Foo<T>(T);
impl<T> Event for Foo<T> {}
fn main() {
let bus = EventBus::new();
register_hook!(&bus, 0, Foo<Cow<str>>, |x| { println!("{}", x); });
post_event!(&bus, Foo(Cow::Borrowed("foo")), Foo<Cow<str>>);
}
And how do I make this compile?
This compiles:
use std::borrow::Cow;
#[derive(Debug)]
struct Foo<T>(T);
impl<T: 'static> Event for Foo<T> {}
fn main() {
let bus = EventBus::new();
register_hook!(&bus, 0, Foo<Cow<str>>, |x: &mut Foo<Cow<'static, str>>| { println!("{:?}", x); });
post_event!(&bus, &mut Foo(Cow::Borrowed("foo")), Foo<Cow<str>>);
}
How do I make this compile?
struct Foo<T>(T);
impl<T> Event for Foo<T> {}
fn main() {
let bus = EventBus::new();
register_hook!(&bus, 0, Foo<Cow<str>>, |x| { println!("{}", x.0); });
let s = format!("{:?}", TypeId::of::<Foo<Cow<str>>>());
post_event!(&bus, Foo(Cow::Borrowed(&s)), Foo<Cow<str>>);
}
[Note: It would be easier if you could use the working code above as a starting point, so I don't need to re-apply the same fixes before I can see the actual problem.]
Foo(Cow::Borrowed(&s))
contains a non-'static
reference, so it can't meet the 'static
bound on the Event
trait.
Glancing at the rest of the code, it seems you definitely need a 'static
type when registering a hook, but maybe it could be made optional when posting an event. Maybe you could use a pattern sort of similar to HashMap::get
, where the stored key type K
and the "query" type Q
are not the same, but can be converted to the same type. You'd need a 'static
bound on the type used in your AnyMap, but then provide some way to do lookups using other types, maybe via the ToOwned::Owned
associated type.
So can we get for<'a>
types instead?
If I recall correctly, for <'a>
refers to any 'a
, which doesn't exactly mean that it is 'static
, therefore breaking what @mbrubeck mentioned about a 'static
reference, making it impossible, unless you should edit the Event
trait from source.
my point is that a hypothetical Fn(&mut for<'a> Foo<'a>)
would be considered 'static. (and a Bar<T>
containing an Fn(&mut T)
would also be 'static for Bar<for<'a> Foo<'a>>
)
Actually, because the Fn
has a lifetime, its own lifetime cannot be greater than the smallest lifetime it encompasses, meaning no 'static
there.
uh right whatever I ate the + 'static stuff
A 'static
lifetime is just a lifetime that's unrestricted. So in
let x = 2;
you can think of x
s lifetime as static, since there's no restrictions on it (it still only lives until x
goes out of scope, but this isn't an artificial restriction). You can also think of objects with a static lifetime as objects that own all their data.
Well in that case, it's 2
who has the 'static
lifetime, not x, because x goes out of scope while 2
is hard-coded into the binary, similar idea to strings, and consts
The problem isn't the lifetime in the Fn
type. Types like for<'a> Fn(&'a T) + 'static
are perfectly valid. (For example in this playground the closure satisfies a Fn
bound containing a lifetime parameter but also satisfies a 'static bound.) The problem in the broken example was caused by code elsewhere trying to take the TypeId
of the Cow<'a, str>
type, which does not meet the required 'static bound.
There's a difference between a value being static, and a type satisfying a 'static bound. As @derekdreery said, a type is 'static as long as it contains no references with restricted lifetimes. So a type like Vec<T>
is 'static even though any particular value of that type lives for shorter than the 'static lifetime. (Playground example.) Lifetime bounds on types are upper bounds rather than lower bounds. A value of a 'static type is allowed to live for up to the 'static lifetime, but it is not required to live that long.
For Any
and TypeId
, it's the 'static bound on the type that matters, because the compiler has no way to generate distinct IDs for every possible Foo<'a>
.
Fun fact, this works:
fn main() {
fn z<'a, 'b>(x: &'a mut std::borrow::Cow<'b, str>) { println!("{}", x); }
let x: &(dyn for<'a> Fn(&'a mut std::borrow::Cow<str>) + 'static) = &z;
let y: &dyn std::any::Any = &x as &dyn std::any::Any;
(y.downcast_ref::<&(dyn for<'a> Fn(&'a mut std::borrow::Cow<str>) + 'static)>().unwrap())(&mut std::borrow::Cow::Borrowed(&*format!("{}", "Foo")));
}
(compiler infers a hidden lifetime in the for<'a> Fn(&'a mut std::borrow::Cow<str>) + 'static
, turning it into for<'a, 'r> Fn(&'a mut std::borrow::Cow<'r, str>) + 'static
)
This (supposedly) equivalent code doesn't:
type MyFn<T> = dyn for<'a> Fn(&'a mut T) + 'static;
fn main() {
fn z<'a, 'b>(x: &'a mut std::borrow::Cow<'b, str>) { println!("{}", x); }
let x: &MyFn<std::borrow::Cow<str>> = &z;
let y: &dyn std::any::Any = &x as &dyn std::any::Any;
(y.downcast_ref::<&MyFn<std::borrow::Cow<str>>>().unwrap())(&mut std::borrow::Cow::Borrowed(&*format!("{}", "Foo")));
}
This is a limitation of Rust, and one that should be fixed. This limitation disallows a lot of perfectly safe code and requires the use of dangerously unsafe workarounds.
And then there's this:
fn main() {
type MyFn<T> = dyn for<'a> Fn(&'a mut T) + 'static;
type MyFnCowstr = dyn for<'a> Fn(&'a mut std::borrow::Cow<str>) + 'static;
let foo: &MyFnCowstr = &|a| println!("{}", a);
let bar: &MyFn<_> = foo;
// this works
bar(&mut std::borrow::Cow::Borrowed(&String::new()));
let foo_any: &std::any::Any = &foo as &std::any::Any;
let foo_back = foo_any.downcast_ref::<&MyFnCowstr>().unwrap();
// this works
foo_back(&mut std::borrow::Cow::Borrowed(&String::new()));
// UNCOMMENT the following code to break the code above.
/*
let bar_any: &std::any::Any = &bar as &std::any::Any;
// */
}