Storing arbitrary Fn in Any

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

playground link

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=2ce2e87bd13598f54a27fefde6c8465b

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.

2 Likes

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);
}
1 Like

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 xs 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.

1 Like

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

2 Likes

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>.

2 Likes

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;
    // */
}