Is there a wrapper type for Rust variables?


#1

Hi
I’m trying to create some struct with general “data” in it for using as a data container.
For example I can write

// in C/C++
typedef struct Test {
    void *data  // cast to anything
}

// in Go
type Test struct {
   data interface{}  // cast to anything
}

And almost all languages that I’ve been worked with have that kind of “base” type which can “wrap” other types.
What can I do to make it with Rust ?


#2

The closest thing should be Any I think: https://doc.rust-lang.org/std/any/index.html


#3

Cool ! thanks it worked, but I’ve added Box, because compiler “suggested”.

let data: Box<Any> = Box::new(String::from("test cast text")) as Box<Any>;

match data.downcast::<String>() {
     Ok(s) => println!("{:?}", s),
     Err(e) => println!("{:?}", e)
}

#4

Just a small thing: either the type annotation (data: Box<Any>) or the explicit cast (as Box<Any>) are sufficient, you don’t need both.


#5

Stupid question: Why is T not sufficient?


#6

@StefanoD because I’m sending this struct as a command to implement, and based on command type it will contain different type of data, but I need to fix main command struct, so I can’t use T

struct Command {
   code: u32,
   data: Box<Any>
}

#7

Commands are usually implemented with enums. If you need something truly arbitrary, consider sending a closure.


#8

Didn’t thought about that ! In terms of code it is better than my “non-functional” solution. But is it affects performance in Rust’s implementation ?
For example in C++11 they have Lambda expressions for functional programming, but they affecting performance a lot.


#9

Its hard to say with certainty (you should benchmark), but I doubt it will be significantly less performant. I’m guessing what you’re doing now is something like this:

match self.code {
    0 => match self.data.downcast_ref::<Foo>() { ... },
    1 => match self.data.downcast_ref::<Bar>() { ... },
    2 => match self.data.downcast_ref::<Baz>() { ... },
    ...
}

Each call therefore involves the match dispatch and then a downcast_ref. The downcast_ref is a virtual call. By using a boxed closure, you can just avoid the matching on self.code, and then you will have 1 virtual call. In other words, in the abstract it seems possibly more performant.

I don’t know what the semantics of C++ lambdas are, but Rust’s are a very efficient, first class part of the language. You should not shy away from using them.

In general, also, Any is almost never the right solution. It doesn’t let the type system help you.


#10

Also here a (quick 'n dirty) example of how it could look with an enum:

struct User;

struct Position;

enum Command {
	Login {
		user: User,
	},
	Logout {
		user: User,
	},
	ChatMessage {
		user: User,
		message: String,
	},
	PlayerMove {
		user: User,
		from: Position,
		to: Position,
	},
}

fn main() {
	let cmd = Command::ChatMessage {
		user: User {},
		message: "something".to_owned(),
	};

	use Command::*;

	match cmd {
		Login {user} => {},
		Logout {user} => {},
		ChatMessage {user,message} => {},
		PlayerMove {user,from,to} => {},
	}
}


#11

If your data type depends only on the command, maybe you should just pass an enum and incorporate the desired type in each variant.

enum Command {
    Nop,
    Bling(u8),
    Splash(i64),
    Bang(MyInvolvedType),
}

#12

Sorry, where did you hear that? Lambdas do not have any particularly great overhead in C++.