Reputation: 117
Is it possible to create an instance of a generic parameter type in Rust?
I'm coming from a C++ background where it's perfectly valid to use template types to create types in the actual function body.
I'm trying to create a variable with type T
in this function but I'm not sure how.
I just want to be able to create an object of type T
, load it, and then insert it into the HashMap
:
fn load<T>(&mut self, id: String, path: String)
where T: AssetTrait + 'static
{
// let asset : T = T; this doesn't work?
asset.load(path);
// self.assets.insert(id, Box::new<T>(asset));
}
Here is all my code:
trait AssetTrait {
fn load(&self, path: String) {
// Do nothing
// Implement this in child asset object
}
}
struct AssetManager {
assets: HashMap<String, Box<AssetTrait + 'static>>,
}
impl AssetManager {
fn new() -> AssetManager {
let asset_manager = AssetManager { assets: HashMap::new() };
return asset_manager;
}
fn load<T>(&mut self, id: String, path: String)
where T: AssetTrait + 'static
{
// let asset : T = T; this doesn't work?
asset.load(path);
// self.assets.insert(id, Box::new<T>(asset));
}
}
Upvotes: 5
Views: 3891
Reputation: 11177
In C++ when you declare a variable like T asset;
you are assuming that T
has a default constructor (compile time version of duck typing). It is an compiler error to instantiated T
with a type that does not have a default constructor, but it's ok if no such instantiation happens.
In Rust you cannot "assume" that a type parameter supports an operation. You have to specify the supported operations with bounds on the type parameter.
That said, you have to options:
Define your own constructor like associated function in AssetTrait
For example, you can declare load
as an associated function that do not take a self
parameter and returns Self
, and call T::load(path)
to instantiate T
:
use std::collections::HashMap;
trait AssetTrait {
fn load(path: String) -> Self;
}
struct AssetManager {
assets: HashMap<String, Box<AssetTrait + 'static>>,
}
impl AssetManager {
fn new() -> AssetManager {
let asset_manager = AssetManager { assets: HashMap::new() };
return asset_manager;
}
fn load<T>(&mut self, id: String, path: String)
where T: AssetTrait + 'static
{
let asset = T::load(path);
self.assets.insert(id, Box::new(asset));
}
}
Use a predefined trait that has a constructor like associated function
In Rust, the Default
trait is used for this purpose:
use std::collections::HashMap;
trait AssetTrait {
fn load(&mut self, path: String);
}
struct AssetManager {
assets: HashMap<String, Box<AssetTrait + 'static>>,
}
impl AssetManager {
fn new() -> AssetManager {
let asset_manager = AssetManager { assets: HashMap::new() };
return asset_manager;
}
fn load<T>(&mut self, id: String, path: String)
where T: Default + AssetTrait + 'static
{
let mut asset = T::default();
asset.load(path);
self.assets.insert(id, Box::new(asset));
}
}
Upvotes: 5