Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experience report: Integration with Any #2

Closed
crlf0710 opened this issue Aug 28, 2021 · 3 comments
Closed

Experience report: Integration with Any #2

crlf0710 opened this issue Aug 28, 2021 · 3 comments

Comments

@crlf0710
Copy link
Member

crlf0710 commented Aug 28, 2021

Code description

By using Any as a supertrait, this feature enables quite nice conversions (upcasts and downcasts) between types in a 2-level depth trait-type taxonomy:

use std::any::Any;

trait Node: Any {}

#[derive(Debug)]
struct Node1 {
    data: i32,
}

impl Node for Node1 {}

#[derive(Debug)]
struct Node2 {
    data: f64,
}

impl Node for Node2 {}

fn visit_all_nodes(nodes: &[Box<dyn Node>]) {
    for node in nodes.iter() {
        let node: &dyn Node = &**node;
        if let Some(node1 @ Node1 { .. }) = (node as &dyn Any).downcast_ref::<Node1>() {
            println!("{:?}", node1);
        } else if let Some(node2 @ Node2 { .. }) = (node as &dyn Any).downcast_ref::<Node2>() {
            println!("{:?}", node2);
        } else {
            println!("unknown node");
        }
    }
}

fn main() {
    let nodes: Vec<Box<dyn Node>> =
        vec![Box::new(Node1 { data: 42 }), Box::new(Node2 { data: 99.0 })];
    visit_all_nodes(&nodes[..]);
}

outputs:

Node1 { data: 42 }
Node2 { data: 99.0 }

What worked well

This is much more ergonomic than asking each node to implement as_any_ref and as_any_mut to enable downcasting.

What worked less well

The (node as &dyn Any).downcast_ref syntax still looks a little cumbersome.

@RalfJung
Copy link
Member

This is much more ergonomic than asking each node to implement as_any_ref and as_any_mut to enable downcasting.

FWIW there is a more ergonomic approach that doesn't require upcasting, via an AsAny trait:

pub trait AsAny: Any {
    fn as_any(&self) -> &dyn Any;
    fn as_any_mut(&mut self) -> &mut dyn Any;
}
impl<T: Any> AsAny for T {
    #[inline(always)]
    fn as_any(&self) -> &dyn Any {
        self
    }
    #[inline(always)]
    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }
}

Adding that as a supertrait gives basically the same ergonomics as trait upcasting (each node doesn't have to do anything). But of course not having to define such a trait is much nicer. :)

@RalfJung
Copy link
Member

Also this is unfortunately very easy to get wrong: given x: Box<dyn Node>, how do you downcast? (x as &dyn Any).downcast_ref will build but do the wrong thing. You need (&**x as &dyn Any).downcast_ref. (Yes, two * are needed.)

It helps to add a downcast method on dyn Node, then auto-(de)ref apply. But ideally that's boilerplate one would like to avoid...

@crlf0710
Copy link
Member Author

Also this is unfortunately very easy to get wrong: given x: Box<dyn Node>, how do you downcast?

I usually write (x.as_ref() as &dyn Any).downcast_ref because i don't like two consecutive *, but that's just my own two cents.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants