Bria
January 12, 2023, 3:27am
1
I used to write Julia(https://julialang.org/ ), it has a nice coding type called Mutiple Dispatch. For that, the a function can have different method according to the type of parameters. An example:
struct DataSave
save1::Dict{Int32, Float32}
save2::Dict{String, Float32}
end
Base.haskey(x::DataSave, i::Int32) = haskey(x.save1, i) // dispatch method haskey on (DataSave, Inti32)
Base.haskey(x::DataSave, i::String) = haskey(x.save2, i) // dispatch method haskey on (DataSave, String)
data_save = DataSave(Dict(1 => 1f32), Dict("a" => 1f32));
haskey(data_save, Int32(1))
true
haskey(data_save, Int32(2))
false
haskey(data_save, "a")
true
I tried to realize it in Rust, I get to this:
struct DataSave {
save1: HashMap<i32, f32>,
save2: HashMap<String, f32>,
}
trait HasKey<T> {
fn has_key(&self, y: &DataSave) -> bool;
}
impl HasKey<i32> for i32 {
fn has_key(&self, y: &DataSave) -> bool {
y.save1.contains_key(self)
}
}
impl HasKey<String> for String {
fn has_key(&self, y: &DataSave) -> bool {
y.save2.contains_key(self)
}
}
impl DataSave {
fn contains_key<T: HasKey<T>>(&self, x: T) -> bool {
x.has_key(self)
}
}
let mut save1 = HashMap::new();
save1.insert(1i32, 2f32);
let mut save2 = HashMap::new();
save2.insert(String::from("a"), 2f32);
let data_save = DataSave { save1, save2 };
data_save.contains_key(1i32)
true
data_save.contains_key(2i32)
false
data_save.contains_key(String::from("a"))
true
But as can be seen, the code in Rust seems much verbose than that in Julia. So I want to ask is there a more brifer way or a official way I can do that in Rust?
Implementing traits has always been verbose in Rust. FWIW, the type parameter on your HasKey
trait is redundant, since the trait is already parameterized by the Self
type it's implemented on (Rust Playground ):
use std::collections::HashMap;
struct DataSave {
save1: HashMap<i32, f32>,
save2: HashMap<String, f32>,
}
trait DataSaveKey {
fn contained_in(&self, x: &DataSave) -> bool;
}
impl DataSave {
fn contains_key<K: ?Sized + DataSaveKey>(&self, key: &K) -> bool {
key.contained_in(self)
}
}
impl DataSaveKey for i32 {
fn contained_in(&self, x: &DataSave) -> bool {
x.save1.contains_key(self)
}
}
impl DataSaveKey for String {
fn contained_in(&self, x: &DataSave) -> bool {
x.save2.contains_key(self)
}
}
// allow `contains_key()` to be called directly with a string literal
impl DataSaveKey for str {
fn contained_in(&self, x: &DataSave) -> bool {
x.save2.contains_key(self)
}
}
Perhaps the most common way to shorten repeated impl blocks is to generate them with a macro . In this example, it might look something like this (Rust Playground ):
use std::collections::HashMap;
struct DataSave {
save1: HashMap<i32, f32>,
save2: HashMap<String, f32>,
}
trait DataSaveKey {
fn contained_in(&self, x: &DataSave) -> bool;
}
impl DataSave {
fn contains_key<K: ?Sized + DataSaveKey>(&self, key: &K) -> bool {
key.contained_in(self)
}
}
macro_rules! data_save_key_impl {
($field:ident, $K:ty) => {
impl DataSaveKey for $K {
fn contained_in(&self, x: &DataSave) -> bool {
x.$field.contains_key(self)
}
}
};
}
data_save_key_impl!(save1, i32);
data_save_key_impl!(save2, String);
// allow `contains_key()` to be called directly with a string literal
data_save_key_impl!(save2, str);
5 Likes
system
Closed
April 12, 2023, 4:11am
3
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.