What is the Rust equivalent for abstract classes? - rust

I am still a beginner in Rust and I am stuck on a point concerning the traits.
Despite many attempts, I can't find a code equivalent in rust for abstract classes.
Here is an example of Typescript code using them:
export interface NodeConstructor<T> {
new (data: T): Node<T>;
}
export abstract class Node<T> {
public data: T;
public key: string;
public parentKeys: string[];
public childKeys: string[];
public parents: Node<T>[];
public children: Node<T>[];
constructor(data: T) {
this.data = data;
this.key = this.buildNodeKey();
this.parentKeys = this.buildParentKeys();
this.childKeys = this.buildChildKeys();
this.parents = [];
this.children = [];
}
get hasParents(): boolean {
return !!this.parents.length;
}
get hasChildren(): boolean {
return !!this.children.length;
}
abstract buildNodeKey(): string;
abstract buildChildKeys(): string[];
abstract buildParentKeys(): string[];
}
Thanks to the 'mwlon' solutions in this post, I arrived at this result:
pub struct Node<T, BUILDER: ?Sized> where BUILDER: NodeBuilder {
pub data: T,
pub key: String,
pub parent_keys: Box<[String]>,
pub child_keys: Box<[String]>,
pub parents: Option<Box<[T]>>,
pub children: Option<Box<[T]>>,
builder: BUILDER,
}
pub trait NodeBuilder {
fn build_node_key(&self) -> String;
fn build_parent_key(&self) -> Box<[String]>;
fn build_child_key(&self) -> Box<[String]>;
}
impl<T , BUILDER> Node<T, BUILDER> where BUILDER: NodeBuilder {
pub fn new(&self, data: T) -> Node<T, BUILDER> {
Self{
data: data,
key: BUILDER::build_node_key(&self.builder),
parent_keys: BUILDER::build_parent_key(&self.builder),
child_keys: BUILDER::build_child_key(&self.builder),
parents: None,
children: None,
builder: self.builder
}
}
pub fn has_parents(&self) -> bool {
match &self.parents {
Some(_x) => true,
None => false,
}
}
pub fn has_children(&self) -> bool {
match &self.children {
Some(_x) => true,
None => false,
}
}
}
Which implements like this:
struct TestModel {
name: String,
children: Option<Box<[String]>>,
parents: Option<Box<[String]>>
}
impl node::Node<TestModel, dyn node::NodeBuilder> {
fn build_child_key(data: TestModel) -> Box<[String]> {
match data.children {
Some(x) => x.clone(),
None => Box::new([]),
}
}
fn build_node_key(data: TestModel) -> String {
data.name.clone()
}
fn build_parent_key(data: TestModel) -> Box<[String]> {
match data.parents {
Some(x) => x.clone(),
None => Box::new([]),
}
}
}
But I still have one error I can't get over:
cannot move out of `self.builder` which is behind a shared reference
move occurs because `self.builder` has type `BUILDER`, which does not implement the `Copy` traitrustc(E0507)
node.rs(28, 22): move occurs because `self.builder` has type `BUILDER`, which does not implement the `Copy` trait
I can't implement 'Copy' on Builder since it is also a trait. Is there something I'm missing?
What are the best practices for such a structure in Rust?
I'm using rustc 1.59.0 (9d1b2106e 2022-02-23)

An abstract class couples together a piece of data which has the same shape for every instance of the type, and a set of behaviors which may differ between different instances of the type. This is a relatively unusual pattern in Rust, because Rust encourages decoupling of data and behavior more than other languages.
A more idiomatic translation of what you are trying to do would probably be along these lines. First, we make a type that holds the data representing a node:
pub struct Node<T> {
data: T,
key: String,
parent_keys: Box<[String]>,
child_keys: Box<[String]>,
parents: Option<Box<[T]>>,
children: Option<Box<[T]>>,
}
We will need a way to create instances of this type, so we will give it a constructor. I do not know exactly how you intend parents and children fields to be filled, so I will leave them as None for now. However, if they are to be filled by reading data from an external source using parent_keys and child_keys, this constructor could be the right place to do that.
impl<T> Node<T> {
pub fn new(data: T, key: String, parent_keys: Box<[String]>, child_keys: Box<[String]>) -> Node<T> {
Node { data, key, parent_keys, child_keys, parents: None, children: None }
}
}
Next, we want a trait to abstract over possible behaviors. In your case, the behavior appears to be 'a way to create a node.' A trait should have exactly the methods necessary to implement its behavior, so:
pub trait NodeBuilder {
fn build_node<T>(&self, data: T) -> Node<T>;
}
We can use generic parameters bounded by NodeBuilder in methods or structs to abstract over types that are capable of building a node. And we can define which types are capable of this by implementing NodeBuilder for them, like so:
struct TestModel {
name: String,
children: Option<Box<[String]>>,
parents: Option<Box<[String]>>
}
impl NodeBuilder for TestModel {
fn build_node<T>(&self, data: T) -> Node<T> {
let parents = self.parents.clone().unwrap_or_else(Default::default);
let children = self.children.clone().unwrap_or_else(Default::default);
Node::new(data, self.name.clone(), parents, children)
}
}
As you can see, this solution avoids coupling data and behavior when it is not necessary. It is, however, specific to the particular situation you have. A different abstract class might translate to a different set of types and traits. This is common when translating across programming paradigms: the role of one pattern in language A might be filled by several patterns in language B, or vice versa.
Instead of focusing too much on how to replicate a pattern like 'abstract classes' in Rust, you should ask yourself what problem that pattern was solving in TypeScript, and consider how to best solve the same problem in Rust, even if that means using different patterns.

If you truly wish to replicate abstract classes in Rust, it is possible. You are on the right track with a trait representing the abstract methods (NodeBuilder) and a struct that is generic over that trait (Node). However, there are a couple issues with the example as written. First, your constructor.
The Node::new method should not take &self, as it creates a new node, it does not initialize one that already exists. Unlike some other languages, Rust does not have constructors which operate on an existing object. Instead, object creation is expressed with free functions on a type, typically called new. Some other languages call these functions 'static.'
In order to create a node, you will need a concrete instance of BUILDER to store in self.builder. This should probably be an argument to Node::new. Alternatively, you could make the methods on the NodeBuilder trait static and remove the builder field if you know that the builders themself will never carry state.
Taken together, this should lead to:
pub fn new(builder: BUILDER, data: T) -> Node<T, BUILDER> {
Self {
data: data,
key: builder.build_node_key(),
parent_keys: builder.build_parent_key(),
child_keys: builder.build_child_key(),
parents: None,
children: None,
builder: builder
}
}
Second, implementing the class.
All the 'abstract' methods of the type are encapsulated in the trait NodeBuilder. So rather than implementing additional associated functions on node::Node<TestModel, dyn node::NodeBuilder>, you want to implement NodeBuilder for TestModel:
impl NodeBuilder for TestModel {
fn build_child_key(&self) -> Box<[String]> {
match self.children {
Some(x) => x.clone(),
None => Box::new([]),
}
}
fn build_node_key(&self) -> String {
self.name.clone()
}
fn build_parent_key(&self) -> Box<[String]> {
match self.parents {
Some(x) => x.clone(),
None => Box::new([]),
}
}
}
At this point, you can pass an instance of TestModel to Node::new (along with a piece of data) to create a Node whose abstract functionality is implemented by TestModel.

Related

Rust - Typescript - Keyof

I'm new to Rust and just wondering if there's an equivalent of the keyof (like in TypeScript) operator in Rust.
I don't know if this is possible, but I'm trying to access the key and value of a struct within another struct.
example:
interface Events {
msg:(data:string)=>any,
abort:()=>any
}
class EventEmitter<T>{
on(event: keyof T,callback:T[keyof T])
}
I'm trying to achieve the same on function in rust.
struct Events {
msg: Fn(&str)->(),
abort: Fn()->(),
}
struct EventEmitter<T> {
pub listeners: Vec<Listener<T>>,
}
Context: I'm trying to recreate EventEimtter exactly like node.js & ts
What you're describing is reflection. As mentioned in the comments to your question, Rust does not have reflection as a native feature. Using a String to access members of your struct could potentially create unpredictable or undefined behavior.
If it truly is important to access members of your struct that have the same type, you could look into creating a trait called "Keyable" or something similar. This struct should (probably) look something like this.
pub trait Keyable<T> {
fn get_string(&self, for_key: T) -> Option<&String>;
fn get_i32(&self, key: T) -> Option<&i32>;
}
pub enum UserKeys {
Id,
Username,
Password
}
pub struct User {
id: i32,
username: String,
password: String
}
impl Keyable<UserKeys> for User {
fn get_string(&self, key: UserKeys) -> Option<&String> {
match key {
UserKeys::Username => Some(&self.username),
UserKeys::Password => Some(&self.password),
_ => None
}
}
fn get_i32(&self, key: UserKeys) -> Option<&i32> {
match key {
UserKeys::Id => Some(&self.id),
_ => None
}
}
}
This would create a valid implementation of reflection in Rust. It is worth noting that you would not necessarily have to type all of this by hand; you could look into creating a Derive macro (Rust Book).
You could then add a type bound to your EventEmitter so it becomes:
struct EventEmitter<K, T: Keyable<K>> {
pub listeners: Vec<Listener<T>>,
}
This code says "I want to create a struct that can hold many instances of a Listener for type Keyable (T) with a certain key type (K).
There would still be quite a bit of work to do in order to get your events all connected, but taking care of reflection is a big step.
This is an example I've written that allows a derivation of a struct called ToJson. It allows all implementors to automatically inherit a to_json function that creates a String of all its properties. GitHub

Is there a macro that automatically creates a dictionary from an enum?

An enum is clearly a kind of key/value pair structure. Consequently, it would be nice to automatically create a dictionary from one wherein the enum variants become the possible keys and their payload the associated values. Keys without a payload would use the unit value. Here is a possible usage example:
enum PaperType {
PageSize(f32, f32),
Color(String),
Weight(f32),
IsGlossy,
}
let mut dict = make_enum_dictionary!(
PaperType,
allow_duplicates = true,
);
dict.insert(dict.PageSize, (8.5, 11.0));
dict.insert(dict.IsGlossy, ());
dict.insert_def(dict.IsGlossy);
dict.remove_all(dict.PageSize);
Significantly, since an enum is merely a list of values that may optionally carry a payload, auto-magically constructing a dictionary from it presents some semantic issues.
How does a strongly typed Dictionary<K, V> maintain the discriminant/value_type dependency inherent with enums where each discriminant has a specific payload type?
enum Ta {
K1(V1),
K2(V2),
...,
Kn(Vn),
}
How do you conveniently refer to an enum discriminant in code without its payload (Ta.K1?) and what type is it (Ta::Discriminant?) ?
Is the value to be set and get the entire enum value or just the payload?
get(&self, key: Ta::Discriminant) -> Option<Ta>
set(&mut self, value: Ta)
If it were possible to augment an existing enum auto-magically with another enum of of its variants then a reasonably efficient solution seems plausible in the following pseudo code:
type D = add_discriminant_keys!( T );
impl<D> for Vec<D> {
fn get(&self, key: D::Discriminant) -> Option<D> { todo!() }
fn set(&mut self, value: D) { todo!() }
}
I am not aware whether the macro, add_discriminant_keys!, or the construct, D::Discriminant, is even feasible. Unfortunately, I am still splashing in the shallow end of the Rust pool, despite this suggestion. However, the boldness of its macro language suggests many things are possible to those who believe.
Handling of duplicates is an implementation detail.
Enum discriminants are typically functions and therefore have a fixed pointer value (as far as I know). If such values could become constants of an associated type within the enum (like a trait) with attributes similar to what has been realized by strum::EnumDiscriminants things would look good. As it is, EnumDiscriminants seems like a sufficient interim solution.
A generic implementation over HashMap using strum_macros crate is provided based on in the rust playground; however, it is not functional there due to the inability of rust playground to load the strum crate from there. A macro derived solution would be nice.
First, like already said here, the right way to go is a struct with optional values.
However, for completeness sake, I'll show here how you can do that with a proc macro.
When you want to design a macro, especially a complicated one, the first thing to do is to plan what the emitted code will be. So, let's try to write the macro's output for the following reduced enum:
enum PaperType {
PageSize(f32, f32),
IsGlossy,
}
I will already warn you that our macro will not support brace-style enum variants, nor combining enums (your add_discriminant_keys!()). Both are possible to support, but both will complicate this already-complicated answer more. I'll refer to them shortly at the end.
First, let's design the map. It will be in a support crate. Let's call this crate denum (a name will be necessary later, when we'll refer to it from our macro):
pub struct Map<E> {
map: std::collections::HashMap<E, E>, // You can use any map implementation you want.
}
We want to store the discriminant as a key, and the enum as the value. So, we need a way to refer to the free discriminant. So, let's create a trait Enum:
pub trait Enum {
type DiscriminantsEnum: Eq + Hash; // The constraints are those of `HashMap`.
}
Now our map will look like that:
pub struct Map<E: Enum> {
map: std::collections::HashMap<E::DiscriminantsEnum, E>,
}
Our macro will generate the implementation of Enum. Hand-written, it'll be the following (note that in the macro, I wrap it in const _: () = { ... }. This is a technique used to prevent names polluting the global namespaces):
#[derive(PartialEq, Eq, Hash)]
pub enum PaperTypeDiscriminantsEnum {
PageSize,
IsGlossy,
}
impl Enum for PaperType {
type DiscriminantsEnum = PaperTypeDiscriminantsEnum;
}
Next. insert() operation:
impl<E: Enum> Map<E> {
pub fn insert(discriminant: E::DiscriminantsEnum, value: /* What's here? */) {}
}
There is no way in current Rust to refer to an enum discriminant as a distinct type. But there is a way to refer to struct as a distinct type.
We can think about the following:
pub struct PageSize;
But this pollutes the global namespace. Of course, we can call it something like PaperTypePageSize, but I much prefer something like PaperTypeDiscriminants::PageSize.
Modules to the rescue!
#[allow(non_snake_case)]
pub mod PaperTypeDiscriminants {
#[derive(Clone, Copy)]
pub struct PageSize;
#[derive(Clone, Copy)]
pub struct IsGlossy;
}
Now we need a way in insert() to validate the the provided discriminant indeed matches the wanted enum, and to refer to its value. A new trait!
pub trait EnumDiscriminant: Copy {
type Enum: Enum;
type Value;
fn to_discriminants_enum(self) -> <Self::Enum as Enum>::DiscriminantsEnum;
fn to_enum(self, value: Self::Value) -> Self::Enum;
}
And here's how our macro will implements it:
impl EnumDiscriminant for PaperTypeDiscriminants::PageSize {
type Enum = PaperType;
type Value = (f32, f32);
fn to_discriminants_enum(self) -> PaperTypeDiscriminantsEnum { PaperTypeDiscriminantsEnum::PageSize }
fn to_enum(self, (v0, v1): Self::Value) -> Self::Enum { Self::Enum::PageSize(v0, v1) }
}
impl EnumDiscriminant for PaperTypeDiscriminants::IsGlossy {
type Enum = PaperType;
type Value = ();
fn to_discriminants_enum(self) -> PaperTypeDiscriminantsEnum { PaperTypeDiscriminantsEnum::IsGlossy }
fn to_enum(self, (): Self::Value) -> Self::Enum { Self::Enum::IsGlossy }
}
And now insert():
pub fn insert<D>(&mut self, discriminant: D, value: D::Value)
where
D: EnumDiscriminant<Enum = E>,
{
self.map.insert(
discriminant.to_discriminants_enum(),
discriminant.to_enum(value),
);
}
And trivially insert_def():
pub fn insert_def<D>(&mut self, discriminant: D)
where
D: EnumDiscriminant<Enum = E, Value = ()>,
{
self.insert(discriminant, ());
}
And get() (note: seprately getting the value is possible when removing, by adding a method to the trait EnumDiscriminant with the signature fn enum_to_value(enum_: Self::Enum) -> Self::Value. It can be unsafe fn and use unreachable_unchecked() for better performance. But with get() and get_mut(), that returns reference, it's harder because you can't get a reference to the discriminant value. Here's a playground that does that nonetheless, but requires nightly):
pub fn get_entry<D>(&self, discriminant: D) -> Option<&E>
where
D: EnumDiscriminant<Enum = E>,
{
self.map.get(&discriminant.to_discriminants_enum())
}
get_mut() is very similar.
Note that my code doesn't handle duplicates but instead overwrites them, as it uses HashMap. However, you can easily create your own map that handles duplicates in another way.
Now that we have a clear picture in mind what the macro should generate, let's write it!
I decided to write it as a derive macro. You can use an attribute macro too, and even a function-like macro, but you must call it at the declaration site of your enum - because macros cannot inspect code other than the code the're applied to.
The enum will look like:
#[derive(denum::Enum)]
enum PaperType {
PageSize(f32, f32),
Color(String),
Weight(f32),
IsGlossy,
}
Usually, when my macro needs helper code, I put this code in crate and the macro in crate_macros, and reexports the macro from crate. So, the code in denum (besides the aforementioned Enum, EnumDiscriminant and Map):
pub use denum_macros::Enum;
denum_macros/src/lib.rs:
use proc_macro::TokenStream;
use quote::{format_ident, quote};
#[proc_macro_derive(Enum)]
pub fn derive_enum(item: TokenStream) -> TokenStream {
let item = syn::parse_macro_input!(item as syn::DeriveInput);
if item.generics.params.len() != 0 {
return syn::Error::new_spanned(
item.generics,
"`denum::Enum` does not work with generics currently",
)
.into_compile_error()
.into();
}
if item.generics.where_clause.is_some() {
return syn::Error::new_spanned(
item.generics.where_clause,
"`denum::Enum` does not work with `where` clauses currently",
)
.into_compile_error()
.into();
}
let (vis, name, variants) = match item {
syn::DeriveInput {
vis,
ident,
data: syn::Data::Enum(syn::DataEnum { variants, .. }),
..
} => (vis, ident, variants),
_ => {
return syn::Error::new_spanned(item, "`denum::Enum` works only with enums")
.into_compile_error()
.into()
}
};
let discriminants_mod_name = format_ident!("{}Discriminants", name);
let discriminants_enum_name = format_ident!("{}DiscriminantsEnum", name);
let mut discriminants_enum = Vec::new();
let mut discriminant_structs = Vec::new();
let mut enum_discriminant_impls = Vec::new();
for variant in variants {
let variant_name = variant.ident;
discriminant_structs.push(quote! {
#[derive(Clone, Copy)]
pub struct #variant_name;
});
let fields = match variant.fields {
syn::Fields::Named(_) => {
return syn::Error::new_spanned(
variant.fields,
"`denum::Enum` does not work with brace-style variants currently",
)
.into_compile_error()
.into()
}
syn::Fields::Unnamed(fields) => Some(fields.unnamed),
syn::Fields::Unit => None,
};
let value_destructuring = fields
.iter()
.flatten()
.enumerate()
.map(|(index, _)| format_ident!("v{}", index));
let value_destructuring = quote!((#(#value_destructuring,)*));
let value_builder = if fields.is_some() {
value_destructuring.clone()
} else {
quote!()
};
let value_type = fields.into_iter().flatten().map(|field| field.ty);
enum_discriminant_impls.push(quote! {
impl ::denum::EnumDiscriminant for #discriminants_mod_name::#variant_name {
type Enum = #name;
type Value = (#(#value_type,)*);
fn to_discriminants_enum(self) -> #discriminants_enum_name { #discriminants_enum_name::#variant_name }
fn to_enum(self, #value_destructuring: Self::Value) -> Self::Enum { Self::Enum::#variant_name #value_builder }
}
});
discriminants_enum.push(variant_name);
}
quote! {
#[allow(non_snake_case)]
#vis mod #discriminants_mod_name { #(#discriminant_structs)* }
const _: () = {
#[derive(PartialEq, Eq, Hash)]
pub enum #discriminants_enum_name { #(#discriminants_enum,)* }
impl ::denum::Enum for #name {
type DiscriminantsEnum = #discriminants_enum_name;
}
#(#enum_discriminant_impls)*
};
}
.into()
}
This macro has several flaws: it doesn't handle visibility modifiers and attributes correctly, for example. But in the general case, it works, and you can fine-tune it more.
If you want to also support brace-style variants, you can create a struct with the data (instead of the tuple we use currently).
Combining enum is possible if you'll not use a derive macro but a function-like macro, and invoke it on both enums, like:
denum::enums! {
enum A { ... }
enum B { ... }
}
Then the macro will have to combine the discriminants and use something like Either<A, B> when operating with the map.
Unfortunately, a couple of questions arise in that context:
should it be possible to use enum types only once? Or are there some which might be there multiple times?
what should happen if you insert a PageSize and there's already a PageSize in the dictionary?
All in all, a regular struct PaperType is much more suitable to properly model your domain. If you don't want to deal with Option, you can implement the Default trait to ensure that some sensible defaults are always available.
If you really, really want to go with a collection-style interface, the closest approximation would probably be a HashSet<PaperType>. You could then insert a value PaperType::PageSize.

Can I declare an enum value that takes a String or &str without needing additional functions?

I have an enum with a String:
enum MyLovelyEnum {
Thing(String),
}
For tests, I would like to be able to pass in a &'static str to avoid MyLovelyEnum::Thing("abc".to_string) over and over.
I found that you can do this nicely with structs with a constructor:
// From: https://hermanradtke.com/2015/05/06/creating-a-rust-function-that-accepts-string-or-str.html
struct Person {
name: String,
}
impl Person {
fn new<S: Into<String>>(name: S) -> Person {
Person { name: name.into() }
}
}
fn main() {
let person = Person::new("Herman");
let person = Person::new("Herman".to_string());
}
I know I can use lifetimes or Cow as described in What's the best practice for str/String values in Rust enums? or I can make my own function.
Is there something close to the example in the blog post for enums? e.g.
// this is the kind of thing I am after but this specifically is not correct syntax
enum MyLovelyEnum {
Thing<S: Into<String>>(S)
}
You can create a generic enum:
enum MyLovelyEnum<S>
where
S: Into<String>,
{
Thing(S),
}
MyLovelyEnum::Thing("a");
MyLovelyEnum::Thing("b".to_string());
I likely wouldn't do that in my code, instead opting to create a constructor, much like the blog post you linked:
enum MyLovelyEnum {
Thing(String),
}
impl MyLovelyEnum {
fn thing(s: impl Into<String>) -> Self {
MyLovelyEnum::Thing(s.into())
}
}
MyLovelyEnum::thing("a");
MyLovelyEnum::thing("b".to_string());

Rust returning enum as generic type

I'm attempting to create a struct that holds a collection of Nodes. In order to limit the type, each of these Nodes can hold the value is of the enum type NodeVal.
I can then add accessor functions to the Container struct to get and set the values. However, rather than adding a get_node_f64, get_node_i64, etc, I'm attempting to make a generic function that accepts a type that implements the Num trait.
This does not work seemingly because the val property of Node is NodeVal rather than T. However if I make it T it will be able to be any type, which I want to avoid.
Is there any way to achieve what I want to do or am I structuring this the wrong way?
use std::collections::HashMap;
use num_traits::Num;
pub enum NodeVal {
Str(String),
F64(f64),
Uint64(u64),
Int64(i64),
}
pub struct Node {
id: i32,
val: NodeVal
}
pub struct Container {
nodes: HashMap<i32, Node>
}
impl Container {
pub fn new() -> Self {
Container {
nodes: HashMap::new()
}
}
pub fn get_node_str(&self, key: &i32) -> Option<String> {
match self.nodes.get(key) {
Some(r) => match &r.val {
NodeVal::Str(x) => Some(x.to_string()),
_ => None
},
None => None
}
}
// Does not compile
pub fn get_node_num<T: num_traits::Num>(&self, key: &i32) -> Option<T> {
match self.nodes.get(key) {
Some(r) => match &r.val {
NodeVal::F64(x) | NodeVal::Uint64(x) | NodeVal::Int64(x) => Some(*x),
_ => None
},
None => None
}
}
}
This does not work seemingly because the val property of Node is NodeVal rather than T. However if I make it T it will be able to be any type, which I want to avoid.
What I get is that it doesn't work because x is of a different type in the three variants you're matching, which doesn't make any sense to Rust, it complains that the x in F64 is an f64, the x in Uint64 is an u64 and the x in Int64 is an i64, therefore the type of x makes no sense (it has three incompatible types it can't reconcile).
Your use of trait bounds is also incorrect, trait bounds are a way for the caller to specify types, but get_node_num does not consider that for a single second, it doesn't care what the caller wants.
Plus the reasoning doesn't make sense:
However if I make it T it will be able to be any type, which I want to avoid.
get_node_num decides what the return type is, T is completely useless. get_node_num also can't work, because you can't return a "f64 or u64 or i64" in Rust, except by creating a new enum which stores these alternatives.

Require type parameter to be a struct

I have a function that I've specified a type parameter T on, which I assume to be a struct. I then collect an iterator of T into a Vec.:
pub fn get_matches<T>(
&self,
) -> Vec< T>
{
...
some_iter.
.map(|(k, _v)| T{key:(**k).to_string(), hits: 0})
.collect()
}
I get this error:
96 | .map(|(k, _v)| T{key:(**k).to_string(), hits: 0})
| ^ not a struct, variant or union type
I've tried having the return type be a type parameter, but I can't work out how to get the Vec's element type and instantiate that either. I just want to produce elements of a certain shape (that is with a key: string, and hits: usize) and return a container of whatever the caller is expecting.
Rust generics are different from C++ templates. There are valid reasons why they are different, such as better error reporting and faster compilation.
In C++, template (if we oversimplify) types are not checked at the initial invocation stage, rather the template continues to expand until it is either successful, or runs into an operation that is not supported by that specific type.
While in Rust, types are checked immediately. The specification must be given up-front, which means that any error is caught at the call site, as opposed to deep in a template expansion.
Your specific example assumes that every T should have fields key and hits, but T can be anything starting from primitive types to non-public structs that don't have key or hits fields.
The Rust way of doing things is to declare a trait and use it to specify that the type has certain constructor function for you. In this context the trait will be a zero-cost abstraction, because of static polymorphism.
trait StringConstructable {
fn new(string: String) -> Self;
}
struct Test {
key: String,
hits: usize
}
impl StringConstructable for Test {
fn new(string: String) -> Self {
Test {
key: string,
hits: 0
}
}
}
fn test<T: StringConstructable>() -> T {
T::new("test".to_string())
}
Playground link
Or implement and require From<String> for your T.
struct Test {
key: String,
hits: usize
}
impl From<String> for Test {
fn from(string: String) -> Test {
Test {
key: string,
hits: 0
}
}
}
fn test<T: From<String>>() -> T {
T::from("test".to_string())
}
Playground link

Resources