Expected i32, found associated type

I’m just learning more about traits. I’ve created a gist (https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=aadcfa77eba9529c6ab8cc829ae52dbc)

I’m not sure it is a good solution. Besides, it doesn’t work.

  Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
   --> src/main.rs:120:30
    |
120 |                     match v.overflowing_add(*value) {
    |                                             ^^^^^^ expected i32, found associated type
    |
    = note: expected type `i32`
               found type `<impl Stackful as Stackful>::T`

Do I need GAT for this? How can this be solved in a generic way?
I’m collect arithmetic operations and calculate the result. I need this for example for i32 and f32. I don’t want duplicated code and learn more about generics. I don’t have a problem with turbofish, if this comes necessary, go for it.

For any help I’m thankful.

Make a trait for arithmetic operators and implement it for both u32 and f32
You can then use that trait as an interface to do the math in Calculator.
No GATs required.

1 Like

Could you help me further? I’m not sure how to go from here

use std::collections::BTreeMap;

#[derive(Debug)]
pub enum Operation {
	ADDITION,
	SUBTRACTION,
	DIVISION,
	MULTIPLICATION,
	NONE,
}

pub struct UnitHelperI32 {
	stack: BTreeMap<u64, (i32, Operation)>,
	count: u64,
}

pub struct UnitHelperF32 {
	stack: BTreeMap<u64, (f32, Operation)>,
	count: u64,
}

trait Arithmetic<T> {
    fn new() -> Self;
	fn get_stack(&mut self) -> &BTreeMap<u64, (T, Operation)>;
    fn erase_operation(&mut self, id: u64);
	fn add<V>(&mut self, operand: V) -> u64 where V: Into<T> + Copy;
	fn sub<V>(&mut self, operand: V) -> u64 where V: Into<T> + Copy;
	fn div<V>(&mut self, operand: V) -> u64 where V: Into<T> + Copy;
	fn mul<V>(&mut self, operand: V) -> u64 where V: Into<T> + Copy;
}

impl<T> Arithmetic<T> for UnitHelperI32{
     fn new() -> Self {
        Self {stack: BTreeMap::new(), count: 0}
    }
	fn get_stack(&mut self) -> &BTreeMap<u64, (i32, Operation)> {
		&self.stack
	}
    fn erase_operation(&mut self, id: u64) {
        self.stack.insert(id, (0, Operation::NONE));
    }
	fn add<V>(&mut self, operand: V) -> u64 where V: Into<i32> + Copy {
		self.stack.insert(self.count, (operand.into(), Operation::ADDITION));
		let count = self.count;
		self.count += 1;
		count
	}
	fn sub<V>(&mut self, operand: V) -> u64 where V: Into<i32> + Copy {
		self.stack.insert(self.count, (operand.into(), Operation::SUBTRACTION));
		let count = self.count;
		self.count += 1;
		count
	}
	fn div<V>(&mut self, operand: V) -> u64 where V: Into<i32> + Copy {
		self.stack.insert(self.count, (operand.into(), Operation::DIVISION));
		let count = self.count;
		self.count += 1;
		count
	}
	fn mul<V>(&mut self, operand: V) -> u64 where V: Into<i32> + Copy {
		self.stack.insert(self.count, (operand.into(), Operation::MULTIPLICATION));
		let count = self.count;
		self.count += 1;
		count
	}
}

impl<T> Arithmetic<T> for UnitHelperF32{
    fn new() -> Self {
        Self {stack: BTreeMap::new(), count: 0}
    }
	fn get_stack(&mut self) -> &BTreeMap<u64, (f32, Operation)> {
		&self.stack
	}
    fn erase_operation(&mut self, id: u64) {
        self.stack.insert(id, (0.0, Operation::NONE));
    }
	fn add<V>(&mut self, operand: V) -> u64 where V: Into<f32> + Copy {
		self.stack.insert(self.count, (operand.into(), Operation::ADDITION));
		let count = self.count;
		self.count += 1;
		count
	}
	fn sub<V>(&mut self, operand: V) -> u64 where V: Into<f32> + Copy {
		self.stack.insert(self.count, (operand.into(), Operation::SUBTRACTION));
		let count = self.count;
		self.count += 1;
		count
	}
	fn div<V>(&mut self, operand: V) -> u64 where V: Into<f32> + Copy {
		self.stack.insert(self.count, (operand.into(), Operation::DIVISION));
		let count = self.count;
		self.count += 1;
		count
	}
	fn mul<V>(&mut self, operand: V) -> u64 where V: Into<f32> + Copy {
		self.stack.insert(self.count, (operand.into(), Operation::MULTIPLICATION));
		let count = self.count;
		self.count += 1;
		count
	}
}

struct Calculator {}

impl Calculator {
	pub fn result(&mut self, helper: &mut impl Arithmetic, initial: i32) -> i32 {
		let mut v = initial;
		let mut overflow = false;
		for (_id, (value, operation)) in helper.get_stack() {
			match operation {
				Operation::ADDITION => {
					match v.overflowing_add(*value) {
						(value, false) => v = value,
						(v, true) => { overflow = true; }
					}
				}
				Operation::SUBTRACTION => {
					match v.overflowing_sub(*value) {
						(value, false) => v = value,
						(_, true) => { overflow = true; }
					}
				}
				Operation::DIVISION => {
					match v.overflowing_div(*value) {
						(value, false) => v = value,
						(_, true) => { overflow = true; }
					}
				}
				Operation::MULTIPLICATION => {
					match v.overflowing_mul(*value) {
						(value, false) => v = value,
						(_, true) => { overflow = true; }
					}
				}
				Operation::NONE => {}
			}
			if overflow {
				break;
			}
		}
		v
	}
}

fn main(){
    let mut calculator = Calculator {};
    let mut helper = UnitHelperF32::new();
    helper.add(1.0);
    let id = helper.mul(2000.0);
    helper.sub(10.0);
    helper.erase_operation(id);
    println!(": {:?}",helper.get_stack());
    let result = calculator.result(&mut helper,1000000000);
    println!("result: {:?}", result);
}

Would something like this work

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a2f4fc5826716e370f6c86aee446eda0

The Arithmetic trait handles the operations


On an unrelated note, you can use Vec for an efficient stack instead of a BTreeMap, it would work out better. Like so

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=10516191390149478b21275fb4c4cdce

2 Likes

Nice and clean. I like it very much. Thank you. I will have a thorough look at it tomorrow.

1 Like

Okay, a related issue. Is there a way to make this compile?

 --> src/main.rs:9:16
  |
9 |     if value > 0.0 {
  |                ^ expected type parameter, found integer
  |
  = note: expected type `T`
             found type `{float}`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
trait Arithmetic: Sized {}
impl Arithmetic for f64 {}

fn main() {}

fn f1<T>(value: T) where T: Arithmetic + Copy + std::cmp::PartialOrd + std::fmt::Debug + std::fmt::Display {
    let mut s=String::new();
    if value > 0.0 {
        s.push_str(&format!("..{}..",value));
    }
}

I’m not sure that will help you but try this :

trait Arithmetic: Sized {}
impl Arithmetic for f64 {}

fn main() {}

fn f1<T: From<float>(value: T) where T: Arithmetic + Copy + std::cmp::PartialOrd + std::fmt::Debug + 
  std::fmt::Display {
  let mut s=String::new();
  if value > T::from(0.0) {
    s.push_str(&format!("..{}..",value));
 }
}
1 Like

The problem is, T is PartialOrd<T> but the actual type T is unknown here so you can’t compare T with {float}(non-integer number literal). You can constraint T: PartialOrd<f64> in the signature to let compiler infer the 0.0 written here is actually f64

1 Like

thank you, but I get an error on playground (if I fix the missing >)

cannot find type `float` in this scope --> src/main.rs:6:15

If I add the constraint T: PartialOrd<f64> I loose the generic formulation of the function. Okay, saying 0.0 or 0 then it has already stopped being generic over T. But there is is_negative() for numbers.
So what if I add a new impl for the trait Arithmetic impl Arithmetic for i64 {}. Can I still formulate the function f1 generic?

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6ef1f1a9528f51eaad3c83276232dce2

You want this trait.

https://docs.rs/num-traits/0.2.8/num_traits/sign/trait.Signed.html

2 Likes

Yeah, nice find. It works now. Many thanks to you all.