How to check if a BiDiMapper contains an id or value in an optimized way? - elrond

I am iterating on all the elements so that I have a complexity O(n).
fn has_item(&self, item: &Item<Self::Api>) -> bool {
return self.map_items_tokens().iter().any(|i| &i.0 == item);
}
How can I save gas fees?

A pull request is pending https://github.com/ElrondNetwork/elrond-wasm-rs/pull/844
For the moment, you can add this utility to your smart contract.
use elrond_wasm::{
api::StorageMapperApi,
elrond_codec::{NestedDecode, NestedEncode, TopDecode, TopEncode},
storage::{mappers::BiDiMapper, StorageKey},
storage_get_len,
types::ManagedType,
};
pub trait ContainsUtils<SA, K, V> {
fn contains_id(&self, id: &K, base_key: &[u8]) -> bool;
fn contains_value(&self, value: &V, base_key: &[u8]) -> bool;
}
impl<SA, K, V> ContainsUtils<SA, K, V> for BiDiMapper<SA, K, V>
where
SA: StorageMapperApi,
K: TopEncode + TopDecode + NestedEncode + NestedDecode + 'static + Default + PartialEq,
V: TopEncode + TopDecode + NestedEncode + NestedDecode + 'static + Default + PartialEq,
{
fn contains_id(&self, id: &K, base_key: &[u8]) -> bool {
let mut key = StorageKey::<SA>::new(base_key);
key.append_bytes(b"_id_to_value");
key.append_item(id);
return storage_get_len(key.as_ref()) > 0;
}
fn contains_value(&self, value: &V, base_key: &[u8]) -> bool {
let mut key = StorageKey::<SA>::new(base_key);
key.append_bytes(b"_value_to_id");
key.append_item(value);
return storage_get_len(key.as_ref()) > 0;
}
}
You use it this way,
where mapper_items_token is the same value as your storage key specified in the attribute #[storage_mapper(your key)]
fn has_item(&self, item: &Item<Self::Api>) -> bool {
return self.map_items_tokens().contains_id(item, b"mapper_items_token");
}

Related

How do I create an Iterator for a QuadTree that yields each point, the leaf it is in, and the neighbouring leaves?

I have a simple QuadTree. For readability and to help me understand what is happening, It avoids recursion and has a static depth. The QuadTree stores references to the points that are owned by another container.
struct QuadLeaf<'a> {
vec: Vec<&'a (f32,f32)>,
rect: (f32, f32, f32, f32)
}
struct QuadTwig<'a> {
cells: [QuadLeaf<'a>; 4],
}
struct QuadBranch<'a> {
cells: [QuadTwig<'a>; 4],
}
struct QuadTree<'a> {
cells: [QuadBranch<'a>; 4],
}
Constructing and inserting into this tree is relatively simple. The QuadLeaf is constructed with a bounding rect and an empty vec, and has a method that attempts to insert a point. It returns true if the point is within the rect and has been inserted.
impl<'a> QuadLeaf<'a> {
fn new(rect: (f32, f32, f32, f32)) -> Self {
QuadLeaf {vec: Vec::new(),rect}
}
fn insert(&mut self, point: &'a (f32, f32)) -> bool {
if is_point_in_rect(point.0, point.1, self.rect) {
self.vec.push(point);
true
} else {
false
}
}
}
The QuadTwig new function splits the bounding rect into 4 and creates 4 new QuadLeafs . It's insert function attempts to insert into each leaf, short circuiting when it is successful, and returning false if unsuccessful.
impl<'a> QuadTwig<'a> {
fn new(rect: (f32, f32, f32, f32)) -> Self {
let rects = divide_into_4(rect);
QuadTwig {
cells: [
QuadLeaf::new(rects[0]),
QuadLeaf::new(rects[1]),
QuadLeaf::new(rects[2]),
QuadLeaf::new(rects[3])
]
}
}
fn insert(&mut self, point: &'a (f32, f32)) -> bool {
for cell in self.cells.iter_mut() {
if cell.insert(point) {
return true;
}
}
false
}
}
The implementations for QuadBranch and QuadTree are exactly the same, but the new function just constructs the next level down in the tree. This could be refactored later for less code duplication, but for demonstration purposes I will leave it. I also think it does not matter for the context of this question.
Question:
I want to create an Iterator that yields each point in the tree, and the 9 leaves that it is close to (or inside of).
I have managed to create a simpler version, that just yields each point and the leaf it is in:
/// An Iterator that yields each point and the leaf it is in
struct PointAndLeafIterator<'a> {
ptr: &'a QuadTree<'a>,
index: (usize, usize, usize, usize)
}
/// An Iterator that yields each point and the leaf it is in
impl<'a> Iterator for PointAndLeafIterator<'a> {
/// Returns (point, leaf)
type Item = (&'a (f32, f32), Vec<&'a (f32, f32)>);
/// Starts at (0,0,0,0) and ends at (3, 3, 3, num_points_in_leaf)
/// It increases the index by 1 each time, and if it reaches the end of the cell, it moves to the next cell
fn next(&mut self) -> Option<Self::Item> {
let (branch_index, twig_index, leaf_index, point_index) = &mut self.index;
let branch = &self.ptr.cells[*branch_index];
let twig = &branch.cells[*twig_index];
let leaf = &twig.cells[*leaf_index];
let point = leaf.vec.get(*point_index);
//go through all the points in the leaf
if let Some(point) = point {
*point_index += 1;
return Some((point, leaf.vec.clone()));
}
//if we reach the end of the leaf, go to the next leaf
*point_index = 0;
*leaf_index += 1;
if *leaf_index < 4 {
return self.next();
}
//if we reach the end of the twig, go to the next twig
*leaf_index = 0;
*twig_index += 1;
if *twig_index < 4 {
return self.next();
}
//if we reach the end of the branch, go to the next branch
*twig_index = 0;
*branch_index += 1;
if *branch_index < 4 {
return self.next();
}
//if we reach the end of the tree, we are done
None
}
}
This can be used like this:
fn main() {
let points: Vec<(f32, f32)> = vec![
(0.0, 0.0),
(1.0, 1.0),
(31.0,31.0),
(2.0, 2.0),
(3.0, 3.0),
(32.0,32.0),
];
let mut quadtree = QuadTree::new((0.0, 0.0, 40.0, 40.0));
for point in points.iter() {
quadtree.insert(point);
}
for (point, leaf) in quadtree.into_point_and_leaf_iter() {
println!("Point: {:?}", point);
println!("Leaf: {:?}", leaf);
}
}
However the neighbouring version is proving to be much more difficult. How can I write this algorithm?
/// An Iterator that yields each point, the leaf it is in, and the neighbouring leaves
struct PointAndLeafAndNeighboursIterator<'a> {
ptr: &'a QuadTree<'a>,
index: (usize, usize, usize, usize)
}
impl<'a> Iterator for PointAndLeafAndNeighboursIterator<'a> {
///Return the 9 leaves that surround the point
///If there is no leaf in a direction, it will return an empty leaf
type Item = (&'a (f32, f32), [Vec<&'a (f32, f32)>; 9]);
/// Starts at (0,0,0,0) and ends at (3, 3, 3, num_points_in_leaf)
/// It increases the index by 1 each time, and if it reaches the end of the cell, it moves to the next cell
fn next(&mut self) -> Option<Self::Item> {
unimplemented!()
}
}
Here is a Rust playground link to all code in this question.
My implementation.
First I added indexing by leaf indices since the QuadTree struct is basically an 8x8 matrix of leaves.
use std::ops::Index;
#[derive(Clone, Copy, Debug)]
struct QuadLeafId {
x: i8,
y: i8,
}
impl QuadLeafId {
fn new(x: i8, y: i8) -> Self {
Self { x, y }
}
fn level_index(&self, level: usize) -> usize {
(((self.y >> level) % 2) * 2 + ((self.x >> level) % 2)) as usize
}
/// Extract the QuadTwig array index containing this leaf
fn twig_index(&self) -> usize {
self.level_index(0)
}
/// Extract the QuadBranch array index containing this leaf
fn branch_index(&self) -> usize {
self.level_index(1)
}
/// Extract the QuadTree array index containing this leaf
fn tree_index(&self) -> usize {
self.level_index(2)
}
}
impl<'a> Index<QuadLeafId> for QuadTwig<'a> {
type Output = QuadLeaf<'a>;
fn index(&self, index: QuadLeafId) -> &Self::Output {
&self.cells[index.twig_index()]
}
}
impl<'a> Index<QuadLeafId> for QuadBranch<'a> {
type Output = QuadLeaf<'a>;
fn index(&self, index: QuadLeafId) -> &Self::Output {
&self.cells[index.branch_index()][index]
}
}
impl<'a> Index<QuadLeafId> for QuadTree<'a> {
type Output = QuadLeaf<'a>;
fn index(&self, index: QuadLeafId) -> &Self::Output {
&self.cells[index.tree_index()][index]
}
}
Then added iterating over nearby leaves in a QuadTree
impl QuadLeafId {
/// The ids of the 9 nearby leaves
fn near_ids(self) -> impl Iterator<Item = Self> {
(-1..=1).flat_map(move |x| {
(-1..=1).map(move |y| Self {
x: self.x + x,
y: self.y + y,
})
})
}
fn is_valid(&self) -> bool {
0 <= self.y && self.y < 8 && 0 <= self.x && self.x < 8
}
}
impl<'a> QuadTree<'a> {
fn get_leaf(&self, id: QuadLeafId) -> Option<&QuadLeaf<'a>> {
id.is_valid().then(|| &self[id])
}
fn near_leaves(&self, id: QuadLeafId) -> impl Iterator<Item = Option<&QuadLeaf<'a>>> {
id.near_ids().map(|id| self.get_leaf(id))
}
}
Implementing the iterator afterwards is straightforward:
impl QuadLeafId {
fn next_id(mut self) -> Option<Self> {
self.x += 1;
if self.x < 8 {
return Some(self);
}
self.x = 0;
self.y += 1;
(self.y < 8).then_some(self)
}
}
impl<'a> QuadTree<'a> {
fn into_point_and_leaf_and_neighbours_iter(
&'a mut self,
) -> PointAndLeafAndNeighboursIterator<'a> {
PointAndLeafAndNeighboursIterator::<'a> {
ptr: self,
index: Some(QuadLeafId::new(0, 0)),
point_index: 0,
}
}
}
/// An Iterator that yields each point, the leaf it is in, and the neighboring leaves
struct PointAndLeafAndNeighboursIterator<'a> {
ptr: &'a QuadTree<'a>,
index: Option<QuadLeafId>,
point_index: usize,
}
impl<'a> Iterator for PointAndLeafAndNeighboursIterator<'a> {
///Return the 9 leaves that surround the point
///If there is no leaf in a direction, it will return an empty leaf
type Item = (&'a (f32, f32), [Vec<&'a (f32, f32)>; 9]);
/// Starts at (0,0,0,0) and ends at (3, 3, 3, num_points_in_leaf)
/// It increases the index by 1 each time, and if it reaches the end of the cell, it moves to the next cell
fn next(&mut self) -> Option<Self::Item> {
loop {
let ind = self.index?;
let leaf = &self.ptr[ind];
if let Some(point) = leaf.vec.get(self.point_index) {
self.point_index += 1;
let mut iter = self
.ptr
.near_leaves(ind)
.map(|leaf| leaf.map(|leaf| leaf.vec.clone()).unwrap_or_default());
return Some((*point, [(); 9].map(|_| iter.next().unwrap())));
}
self.point_index = 0;
self.index = ind.next_id();
}
}
}

Building clean and flexible binary trees in Rust

I'm using binary trees to create a simple computation graph. I understand that linked lists are a pain in Rust, but it's a very convenient data structure for what I'm doing. I tried using Box and Rc<RefCell> for the children nodes, but it didn't work out how I wanted, so I used unsafe:
use std::ops::{Add, Mul};
#[derive(Debug, Copy, Clone)]
struct MyStruct {
value: i32,
lchild: Option<*mut MyStruct>,
rchild: Option<*mut MyStruct>,
}
impl MyStruct {
unsafe fn print_tree(&mut self, set_to_zero: bool) {
if set_to_zero {
self.value = 0;
}
println!("{:?}", self);
let mut nodes = vec![self.lchild, self.rchild];
while nodes.len() > 0 {
let child;
match nodes.pop() {
Some(popped_child) => child = popped_child.unwrap(),
None => continue,
}
if set_to_zero {
(*child).value = 0;
}
println!("{:?}", *child);
if !(*child).lchild.is_none() {
nodes.push((*child).lchild);
}
if !(*child).rchild.is_none() {
nodes.push((*child).rchild);
}
}
println!("");
}
}
impl Add for MyStruct {
type Output = Self;
fn add(self, other: Self) -> MyStruct {
MyStruct{
value: self.value + other.value,
lchild: Some(&self as *const _ as *mut _),
rchild: Some(&other as *const _ as *mut _),
}
}
}
impl Mul for MyStruct {
type Output = Self;
fn mul(self, other: Self) -> Self {
MyStruct{
value: self.value * other.value,
lchild: Some(&self as *const _ as *mut _),
rchild: Some(&other as *const _ as *mut _),
}
}
}
fn main() {
let mut tree: MyStruct;
{
let a = MyStruct{ value: 10, lchild: None, rchild: None };
let b = MyStruct{ value: 20, lchild: None, rchild: None };
let c = a + b;
println!("c.value: {}", c.value); // 30
let mut d = a + b;
println!("d.value: {}", d.value); // 30
d.value = 40;
println!("d.value: {}", d.value); // 40
let mut e = c * d;
println!("e.value: {}", e.value); // 1200
unsafe {
e.print_tree(false); // correct values
e.print_tree(true); // all zeros
e.print_tree(false); // all zeros, everything is set correctly
}
tree = e;
}
unsafe { tree.print_tree(false); } // same here, only zeros
}
Link to the playground
I honestly don't mind that much using unsafe, but is there a safe way doing it? How bad is the use of unsafe here?
You can just box both of the children, since you have a unidirectional tree:
use std::ops::{Add, Mul};
use std::fmt;
#[derive(Clone)]
struct MyStruct {
value: i32,
lchild: Option<Box<MyStruct>>,
rchild: Option<Box<MyStruct>>,
}
impl fmt::Debug for MyStruct {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("MyStruct")
.field("value", &self.value)
.field("lchild", &self.lchild.as_deref())
.field("rchild", &self.rchild.as_deref())
.finish()
}
}
impl MyStruct {
fn print_tree(&mut self, set_to_zero: bool) {
if set_to_zero {
self.value = 0;
}
println!("MyStruct {{ value: {:?}, lchild: {:?}, rchild: {:?} }}", self.value, &self.lchild as *const _, &self.rchild as *const _);
if let Some(child) = &mut self.lchild {
child.print_tree(set_to_zero);
}
if let Some(child) = &mut self.rchild {
child.print_tree(set_to_zero);
}
}
}
impl Add for MyStruct {
type Output = Self;
fn add(self, other: Self) -> MyStruct {
MyStruct {
value: self.value + other.value,
lchild: Some(Box::new(self)),
rchild: Some(Box::new(other)),
}
}
}
impl Mul for MyStruct {
type Output = Self;
fn mul(self, other: Self) -> Self {
MyStruct {
value: self.value * other.value,
lchild: Some(Box::new(self)),
rchild: Some(Box::new(other)),
}
}
}
fn main() {
let tree = {
let a = MyStruct {
value: 10,
lchild: None,
rchild: None,
};
let b = MyStruct {
value: 20,
lchild: None,
rchild: None,
};
let c = a.clone() + b.clone();
println!("c.value: {}", c.value); // 30
let mut d = a.clone() + b.clone();
println!("d.value: {}", d.value); // 30
d.value = 40;
println!("d.value: {}", d.value); // 40
let mut e = c * d;
println!("e.value: {}", e.value); // 1200
println!("");
e.print_tree(false); // correct values
println!("");
e.print_tree(true); // all zeros
println!("");
e.print_tree(false); // all zeros, everything is set correctly
println!("");
e
};
dbg!(tree);
}
I implemented Debug manually and reimplemented print_tree recursively. I don't know if there is a way to implement print_tree as mutable like that without recursion, but it's certainly possible if you take &self instead (removing the set_to_zero stuff).
playground
Edit: Turns out it is possible to mutably iterate over the tree values without recursion. The following code is derived from the playground in this comment by #Shepmaster.
impl MyStruct {
fn zero_tree(&mut self) {
let mut node_stack = vec![self];
let mut value_stack = vec![];
// collect mutable references to each value
while let Some(MyStruct { value, lchild, rchild }) = node_stack.pop() {
value_stack.push(value);
if let Some(child) = lchild {
node_stack.push(child);
}
if let Some(child) = rchild {
node_stack.push(child);
}
}
// iterate over mutable references to values
for value in value_stack {
*value = 0;
}
}
}

How do I check if a slice is sorted?

How do I check if a slice is sorted?
Assuming a function that accepts a slice of i32, is there an idiomatic Rust way of checking if the slice is sorted?
fn is_sorted(data: &[i32]) -> bool {
// ...
}
Would it be possible to generalize the above method so that it would accept an iterator?
fn is_sorted<I>(iter: I)
where
I: Iterator,
I::Item: Ord,
{
// ...
}
I'd grab pairs of elements and assert they are all in ascending (or descending, depending on what you mean by "sorted") order:
fn is_sorted<T>(data: &[T]) -> bool
where
T: Ord,
{
data.windows(2).all(|w| w[0] <= w[1])
}
fn main() {
assert!(is_sorted::<u8>(&[]));
assert!(is_sorted(&[1]));
assert!(is_sorted(&[1, 2, 3]));
assert!(is_sorted(&[1, 1, 1]));
assert!(!is_sorted(&[1, 3, 2]));
assert!(!is_sorted(&[3, 2, 1]));
}
Ditto for generic iterators:
extern crate itertools; // 0.7.8
use itertools::Itertools;
fn is_sorted<I>(data: I) -> bool
where
I: IntoIterator,
I::Item: Ord + Clone,
{
data.into_iter().tuple_windows().all(|(a, b)| a <= b)
}
fn main() {
assert!(is_sorted(&[] as &[u8]));
assert!(is_sorted(&[1]));
assert!(is_sorted(&[1, 2, 3]));
assert!(is_sorted(&[1, 1, 1]));
assert!(!is_sorted(&[1, 3, 2]));
assert!(!is_sorted(&[3, 2, 1]));
}
See also:
Are there equivalents to slice::chunks/windows for iterators to loop over pairs, triplets etc?
In nightly Rust, there are unstable methods to accomplish this:
slice::is_sorted
slice::is_sorted_by
slice::is_sorted_by_key
Iterator::is_sorted
Iterator::is_sorted_by
Iterator::is_sorted_by_key
It is not necessary to have Clone for an iterator is_sorted implementation. Here is a no-dependency Rust implementation of is_sorted:
fn is_sorted<I>(data: I) -> bool
where
I: IntoIterator,
I::Item: Ord,
{
let mut it = data.into_iter();
match it.next() {
None => true,
Some(first) => it.scan(first, |state, next| {
let cmp = *state <= next;
*state = next;
Some(cmp)
}).all(|b| b),
}
}
One more using try_fold():
pub fn is_sorted<T: IntoIterator>(t: T) -> bool
where
<T as IntoIterator>::Item: std::cmp::PartialOrd,
{
let mut iter = t.into_iter();
if let Some(first) = iter.next() {
iter.try_fold(first, |previous, current| {
if previous > current {
Err(())
} else {
Ok(current)
}
})
.is_ok()
} else {
true
}
}

Is there a way to do a for loop that is neither iterative nor linear?

Can I write a Rust for loop equivalent to this C code:
for(int i = 2; i <= 128; i=i*i){
//do something
}
I'm only seeing things like
for i in 0..128 { /* do something */ }
or
let v = vec![0, 1, 2, /* ... */ ];
for i in v.iter() { /* do something */ }
Should I just use a while loop?
You can always create a custom iterator that does whatever unique sequence you need:
struct Doubling {
current: u64,
max: u64,
}
impl Iterator for Doubling {
type Item = u64;
fn next(&mut self) -> Option<Self::Item> {
if self.current > self.max {
None
} else {
let v = Some(self.current);
self.current *= 2;
v
}
}
}
fn main() {
let iter = Doubling { current: 2, max: 128 };
let values: Vec<_> = iter.collect();
println!("{:?}", values);
}
It's important to recognize that this logic (like the original C!) has nasty edge cases when the value is doubled beyond the size of the type.
In this particular case, you can also recognize that you have an exponential series:
fn main() {
let iter = (1..8).map(|p| 2i32.pow(p));
let values: Vec<_> = iter.collect();
println!("{:?}", values);
}
If you want to get really experimental, check out Lazy sequence generation in Rust. Adapted here:
#![feature(generators, generator_trait, conservative_impl_trait)]
use std::ops::{Generator, GeneratorState};
fn doubling(mut start: u64, max: u64) -> impl Iterator<Item = u64> {
GeneratorIteratorAdapter(move || {
while start <= max {
yield start;
start *= 2;
}
})
}
fn main() {
let iter = doubling(2, 128);
let sum: Vec<_> = iter.collect();
println!("{:?}", sum);
}
/* copy-pasta */
struct GeneratorIteratorAdapter<G>(G);
impl<G> Iterator for GeneratorIteratorAdapter<G>
where
G: Generator<Return = ()>,
{
type Item = G::Yield;
fn next(&mut self) -> Option<Self::Item> {
match self.0.resume() {
GeneratorState::Yielded(x) => Some(x),
GeneratorState::Complete(_) => None,
}
}
}
can I write a for loop equivalent to this C code:
That specifically, yes:
extern crate itertools;
for i in itertools::iterate(2, |&i| i*i).take_while(|&i| i <= 128) {
// do something
}
But in general, no. There is no single, direct equivalent to all possible uses of C's for loop. If there's no way to write it using iterators then yes, you need to use a more general loop form:
{
let mut i = 2;
while i <= 128 {
// do something
i = i*i;
}
}

Is it possible to calculate the return type of a Rust function or trait method based on its arguments?

Can I achieve something similar to boost::math::tools::promote_args in Rust? See also Idiomatic C++11 type promotion
To be more specific: is it possible to calculate the return type of a function or trait method based on its arguments and ensure, that the return type has the same type as one of the arguments?
Consider the following case. I have two structs:
#[derive(Debug, Clone, Copy)]
struct MySimpleType(f64);
#[derive(Debug, Clone, Copy)]
struct MyComplexType(f64, f64);
where MySimpleType can be promoted to MyComplexType via the From trait.
impl From<MySimpleType> for MyComplexType {
fn from(src: MySimpleType) -> MyComplexType {
let MySimpleType(x1) = src;
MyComplexType(x1, 0.0)
}
}
I want to write a function that takes two arguments of types MySimpleType or MyComplexType and return a value of type MySimpleType if all arguments are typed as MySimpleType, otherwise the function should return a value of type MyComplexType. Assuming I have implemented Add<Output=Self> for both types I could do something like this:
trait Foo<S, T> {
fn foo(s: S, t: T) -> Self;
}
impl<S, T, O> Foo<S, T> for O
where O: From<S> + From<T> + Add<Output = Self>
{
fn foo(s: S, t: T) -> Self {
let s: O = From::from(s);
let t: O = From::from(t);
s + t
}
}
but then the compiler doesn't know that O should be either S or T and I have to annotate most method calls.
My second attempt is to use a slightly different trait and write two implementations:
trait Foo<S, T> {
fn foo(s: S, t: T) -> Self;
}
impl Foo<MySimpleType, MySimpleType> for MySimpleType {
fn foo(s: MySimpleType, t: MySimpleType) -> Self {
s + t
}
}
impl<S, T> Foo<S, T> for MyComplexType
where MyComplexType: From<S> + From<T>
{
fn foo(s: S, t: T) -> Self {
let s: MyComplexType = From::from(s);
let t: MyComplexType = From::from(t);
s + t
}
}
but again, the compiler isn't able to figure the return type of
Foo::foo(MySimpleType(1.0), MySimpleType(1.0))
The third attempt is something similar to the std::ops::{Add, Mul, ...}. Use an associated type and write a specific implementation for each possible combination of argument types
trait Foo<T> {
type Output;
fn foo(self, t: T) -> Self::Output;
}
impl<T: Add<Output=T>> Foo<T> for T {
type Output = Self;
fn foo(self, t: T) -> Self::Output {
self + t
}
}
impl Foo<MySimpleType> for MyComplexType {
type Output = Self;
fn foo(self, t: MySimpleType) -> Self::Output {
let t: Self = From::from(t);
self + t
}
}
impl Foo<MyComplexType> for MySimpleType {
type Output = MyComplexType;
fn foo(self, t: MyComplexType) -> Self::Output {
let s: MyComplexType = From::from(self);
s + t
}
}
This seems to be the best solution until one needs a function with n arguments. Because then one has to write 2^n - n + 1 impl statements. Of course, this gets even worse if more then two types being considered.
===
Edit:
In my code I've multiple nested function calls and I want to avoid non necessary type promotion, since the evaluation of the functions for the simple type is cheap and expensive for the complex type. By using #MatthieuM. 's proposed solution, this is not achieved. Please consider the following example
#![feature(core_intrinsics)]
use std::ops::Add;
trait Promote<Target> {
fn promote(self) -> Target;
}
impl<T> Promote<T> for T {
fn promote(self) -> T {
self
}
}
impl Promote<u64> for u32 {
fn promote(self) -> u64 {
self as u64
}
}
fn foo<Result, Left, Right>(left: Left, right: Right) -> Result
where Left: Promote<Result>,
Right: Promote<Result>,
Result: Add<Output = Result>
{
println!("============\nFoo called");
println!("Left: {}", unsafe { std::intrinsics::type_name::<Left>() });
println!("Right: {}",
unsafe { std::intrinsics::type_name::<Right>() });
println!("Result: {}",
unsafe { std::intrinsics::type_name::<Result>() });
left.promote() + right.promote()
}
fn bar<Result, Left, Right>(left: Left, right: Right) -> Result
where Left: Promote<Result>,
Right: Promote<Result>,
Result: Add<Output = Result>
{
left.promote() + right.promote()
}
fn baz<Result, A, B, C, D>(a: A, b: B, c: C, d: D) -> Result
where A: Promote<Result>,
B: Promote<Result>,
C: Promote<Result>,
D: Promote<Result>,
Result: Add<Output = Result>
{
let lhs = foo(a, b).promote();
let rhs = bar(c, d).promote();
lhs + rhs
}
fn main() {
let one = baz(1u32, 1u32, 1u64, 1u32);
println!("{}", one);
}
I would expect the simplest way to implement promotion is to create a Promote trait:
trait Promote<Target> {
fn promote(self) -> Target;
}
impl<T> Promote<T> for T {
fn promote(self) -> T { self }
}
Note: I provide a blanket implementation as all types can be promoted to themselves.
Using associated types is NOT an option here, because a single type can be promoted to multiple types; thus we just use a regular type parameter.
Using this, a simple example is:
impl Promote<u64> for u32 {
fn promote(self) -> u64 { self as u64 }
}
fn add<Result, Left, Right>(left: Left, right: Right) -> Result
where
Left: Promote<Result>,
Right: Promote<Result>,
Result: Add<Output = Result>
{
left.promote() + right.promote()
}
fn main() {
let one: u32 = add(1u32, 1u32);
let two: u64 = add(1u32, 2u64);
let three: u64 = add(2u64, 1u32);
let four: u64 = add(2u64, 2u64);
println!("{} {} {} {}", one, two, three, four);
}
The only issue is that in the case of two u32 arguments, the result type must be specified otherwise the compiler cannot choose between which possible Promote implementation to use: Promote<u32> or Promote<u64>.
I am not sure if this is an issue in practice, however, since at some point you should have a concrete type to anchor type inference. For example:
fn main() {
let v = vec![add(1u32, 1u32), add(1u32, 2u64)];
println!("{:?}", v);
}
compiles without type hint, because add(1u32, 2u64) can only be u64, and therefore since a Vec is a homogeneous collection, add(1u32, 1u32) has to return a u64 here.
As you experienced, though, sometimes you need the ability to direct the result beyond what type inference can handle. It's fine, you just need another trait for it:
trait PromoteTarget {
type Output;
}
impl<T> PromoteTarget for (T, T) {
type Output = T;
}
And then a little implementation:
impl PromoteTarget for (u32, u64) {
type Output = u64;
}
impl PromoteTarget for (u64, u32) {
type Output = u64;
}
With that out of the way, we can rewrite baz signature to correctly account for all intermediate types. Unfortunately I don't know any way to introduce aliases in a where clause, so brace yourself:
fn baz<Result, A, B, C, D>(a: A, b: B, c: C, d: D) -> Result
where
A: Promote<<(A, B) as PromoteTarget>::Output>,
B: Promote<<(A, B) as PromoteTarget>::Output>,
C: Promote<<(C, D) as PromoteTarget>::Output>,
D: Promote<<(C, D) as PromoteTarget>::Output>,
(A, B): PromoteTarget,
(C, D): PromoteTarget,
<(A, B) as PromoteTarget>::Output: Promote<Result> + Add<Output = <(A, B) as PromoteTarget>::Output>,
<(C, D) as PromoteTarget>::Output: Promote<Result> + Add<Output = <(C, D) as PromoteTarget>::Output>,
Result: Add<Output = Result>
{
let lhs = foo(a, b).promote();
let rhs = bar(c, d).promote();
lhs + rhs
}
Link to the playground here, so you can check the result:
============
Foo called
Left: u32
Right: u32
Result: u32
4

Resources