Trait for enums convertible to isize - rust

I have several enums. Each of them has numeric tags and can be converted to integer (isize). I have a trait that all of them define. But instances of that trait are not convertible to integer any more, because the compiler can't prove all implementations are actually parameterless enums.
Is there any trait that would mean a type is a parameterless enum or that it is convertible to integer (by any means) that I could use as constraint in the generics that need to get the numeric value?
To be more specific, here is a code-sample (also on play):
enum Foo {
a = 1,
b = 2,
c = 3,
}
enum Bar {
d = 4,
e = 5,
f = 6,
}
trait Process : std::marker::MarkerTrait {
fn process(v: isize) -> String;
}
impl Process for Foo {
fn process(v: isize) -> String { format!("foo{}", v) }
}
impl Process for Bar {
fn process(v: isize) -> String { format!("bar{}", v) }
}
// just for sake of argument; something more complex in reality, of course
fn extern_call(v: isize) -> isize { 2 * v + 211 }
fn process<T: Process>(v: T) -> String {
T::process(extern_call(v as isize))
}
fn main() {
println!("a: {}", process(Foo::a));
}
Obviously the problem is in process, v as isize fails to compile with
error: non-scalar cast: T as isize
So I would like to do something like
process<T: Process + scalar>
or
process<T: Process + ToPrimitive>
or something that would tell the compiler that only types that can be cast to isize are permitted, but I don't know what that could be.

Add a method to your trait that coerces it to a isize. Then the compiler can verify that the objects meet the required criteria:
enum Foo {
A = 1,
B = 2,
C = 3,
}
enum Bar {
D = 4,
E = 5,
F = 6,
}
trait Process: {
fn as_isize(self) -> isize;
fn process(v: isize) -> String;
}
impl Process for Foo {
fn as_isize(self) -> isize { self as isize }
fn process(v: isize) -> String { format!("foo{}", v) }
}
impl Process for Bar {
fn as_isize(self) -> isize { self as isize }
fn process(v: isize) -> String { format!("bar{}", v) }
}
// just for sake of argument; something more complex in reality, of course
fn extern_call(v: isize) -> isize { 2 * v + 211 }
fn process<T: Process>(v: T) -> String {
T::process(extern_call(v.as_isize()))
}
fn main() {
println!("a: {}", process(Foo::A));
}
FYI, MarkerTrait isn't needed here (and is un-idiomatic):
MarkerTrait is intended to be used as the supertrait for traits that don't have any methods

Related

Return only an owned type from a trait method that can accept an owned or borrowed value as input

I want to have a trait that can be implemented for T and &T but has methods that always return T.
What I would like to do is something like this
use std::borrow::ToOwned;
trait Foo<X: ToOwned> {
fn f(&self, x: X) -> f64;
fn g(&self) -> X::Owned;
}
struct Float(f64);
impl Foo<f64> for Float {
fn f(&self, x: f64) -> f64 {
x + self.0
}
fn g(&self) -> f64 {
self.0 * 2.0
}
}
struct List(Vec<f64>);
impl Foo<&Vec<f64>> for List {
fn f(&self, x: &Vec<f64>) -> f64 {
x.iter().sum()
}
// Error here - `&Vec<f64>` return type expected
fn g(&self) -> Vec<f64> {
self.0.iter().map(|&x| 2.0 * x).collect()
}
}
fn main() {
let float = Float(2.0);
println!("{} {}", float.f(3.0), float.g());
let list = List(vec![0.0, 1.0, 2.0]);
println!("{} {:?}", list.f(&vec![1.0, 2.0]), list.g());
}
I know that one option is to have a trait that defines the output type like so
trait FooReturn {
type Output;
}
trait Foo<X: FooReturn> {
fn f(&self, x: X) -> f64;
fn g(&self) -> X::Output;
}
then implement the trait for all relevant types, but I was wondering if there was a more standard/robust way to do this.
This is how you would do it once specialization is complete. Meanwhile, I couldn't even get a simple working example to compile on 1.55.0-nightly.
#![feature(specialization)]
trait MaybeOwned {
type Owned;
}
default impl<X> MaybeOwned for X {
type Owned = X;
}
impl<'a, X> MaybeOwned for &'a X {
type Owned = X;
}
trait Foo<X: MaybeOwned> {
fn f(&self, x: &X) -> f64;
fn g(&self) -> <X as MaybeOwned>::Owned;
}

Generalizing over adding unsigned and signed integer types

I want to have a Rust function that allows adding an u32 (u64, u128) type to an i32 (i64, i128) type while checking for overflow.
My implementation:
/// Add u32 to i32. In case of an overflow, return None.
fn checked_add_i32_u32(a: i32, b: u32) -> Option<i32> {
let b_half = (b / 2) as i32;
let b_rem = (b % 2) as i32;
Some(a.checked_add(b_half)?.checked_add(b_half)?
.checked_add(b_rem)?)
}
/// Add u64 to i64. In case of an overflow, return None.
fn checked_add_i64_u64(a: i64, b: u64) -> Option<i64> {
let b_half = (b / 2) as i64;
let b_rem = (b % 2) as i64;
Some(a.checked_add(b_half)?.checked_add(b_half)?
.checked_add(b_rem)?)
}
I have another similar one that does the same for u128 and i128. I feel like I am repeating myself. My tests for those functions also look very similar.
Is there a way I could refactor my code and have just one function instead? I am not sure how to generalize over the relationship between u32 and i32 (or u64 and i64, u128 and i128).
You can use a macro:
trait CustomAdd: Copy {
type Unsigned;
fn my_checked_add(self, b: Self::Unsigned) -> Option<Self>;
}
macro_rules! impl_custom_add {
( $i:ty, $u:ty ) => {
impl CustomAdd for $i {
type Unsigned = $u;
fn my_checked_add(self, b: $u) -> Option<$i> {
let b_half = (b / 2) as $i;
let b_rem = (b % 2) as $i;
Some(self.checked_add(b_half)?.checked_add(b_half)?
.checked_add(b_rem)?)
}
}
}
}
impl_custom_add!(i32, u32);
impl_custom_add!(i64, u64);
// etc.
#[test]
fn tests() {
assert_eq!(123.my_checked_add(10_u32), Some(133));
}

"no method found for type T in the current scope" when wrapping a type

I am trying to make adapters around 2 different types that do the same job and I cannot rewrite the two types.
X has a method which consumes self so a run-time polymorphic wrapper is not applicable. The only option is a static generic approach.
struct X {}
impl X {
fn f(self, n: i32) {
println!("n = {}", n);
}
fn new() -> X {
X {}
}
}
struct AdapterX {
x: X
}
impl AdapterX {
fn uf(self, n: i32) {
self.x.f(n)
}
fn new(x: X) -> AdapterX {
AdapterX { x: x }
}
}
fn useAdapted<T>(a: T) {
a.uf(10)
}
fn main() {
let x = X::new();
useAdapted::<AdapterX>(AdapterX::new(x));
}
The compiler fails with:
error: no method named `uf` found for type `T` in the current scope
a.uf(10)
^~
The problem is here:
fn useAdapted<T>(a: T) {
a.uf(10)
}
This says
give me any possible type, and I will call the uf method on it
That's clearly nonsense, as you could pass in a String or bool or HashMap or a File or a .... (you get the point).
There's no method uf that applies to every type, so the compiler tells you so. As you discovered, you have to provide a bound on the generic type with one or more traits. Methods and associated functions from those traits will be usable inside the method.
Also note that the Rust style is snake_case; the function should be called use_adapted.
I was able to figure it out; the wrapper struct is not needed. The right way is a generic trait. I also missed the type scope for the generic type variable.
struct X {}
impl X {
fn f(self, n: i32) {
println!("n = {}", n);
}
fn new() -> X {
X {}
}
}
trait Adapter<T> {
fn uf(self, n: i32);
}
impl Adapter<X> for X {
fn uf(self, n: i32) {
self.f(n)
}
}
struct Y {}
impl Y {
fn g(self, n: f32) {
println!("m = {}", n);
}
fn new() -> Y {
Y {}
}
}
impl Adapter<Y> for Y {
fn uf(self, n: i32) {
self.g(n as f32)
}
}
fn use_adapted<A, T: Adapter<A>>(a: T) {
a.uf(10)
}
fn main() {
use_adapted(X::new());
use_adapted(Y::new());
}

Can you implement a generic struct for multiple types?

I have a generic struct Dit<T> that implements the FFT for T:
struct Dit<T> {
n: usize,
exponents: Bin<f32, Complex<T>>,
tmp: Option<Vec<Complex<T>>>,
}
impl Dit<f32> {
/// Create a new instance
///
/// Notice that the number of samples that will be processed by an instance
/// is always fixed, because the exponent values are precalculated.
///
/// # Parameters
/// - `n` The number of samples this operator can process, eg. 1024
pub fn new(n: usize) -> Result<Dit<f32>, FFTError> {
if 2.pow((n as f64).log2() as usize) != n {
return Err(FFTError::InvalidLength);
}
let rtn = Dit {
n: n,
exponents: Bin::new(),
tmp: None,
}.pregen();
return Ok(rtn);
}
// ...
}
I started to add the implementation for f64:
impl Dit<f64> {
pub fn new(n: usize) -> Result<Dit<f64>, FFTError> {
unimplemented!()
}
// ...
}
...and I'm getting these sorts of errors:
src/impls/dit.rs:186:7: 196:4 error: duplicate definition of value `new`
src/impls/dit.rs:186 pub fn new(n:usize) -> Result<Dit<f64>, FFTError> {
src/impls/dit.rs:187 if 2.pow((n as f64).log2() as usize) != n {
src/impls/dit.rs:188 return Err(FFTError::InvalidLength);
src/impls/dit.rs:189 }
src/impls/dit.rs:190 let rtn = Dit {
src/impls/dit.rs:191 n: n,
...
src/impls/dit.rs:110:7: 120:4 note: first definition of value `new` here
src/impls/dit.rs:110 pub fn new(n:usize) -> Result<Dit<f32>, FFTError> {
src/impls/dit.rs:111 if 2.pow((n as f64).log2() as usize) != n {
src/impls/dit.rs:112 return Err(FFTError::InvalidLength);
src/impls/dit.rs:113 }
src/impls/dit.rs:114 let rtn = Dit {
src/impls/dit.rs:115 n: n,
I am confused. My impression was that for the generic Foo<T>, the implementation Foo<Bar1> is a different concrete instance to the implementation of Foo<Bar2>. As such, I was under the impression that I could have different instances of methods for each concrete instance.
What am I doing wrong?
I don't think that it is possible to solve your task with such syntax (at least, I cannot find any examples in the rust reference book).
But there are some working constructions like:
impl<T> Dit<T> where T: Float {
or:
trait DitTrait {
fn new(n: usize) -> Result<Self, FFTError>;
}
impl DitTrait for Dit<f32> { ... }
impl DitTrait for Dit<f64> { ... }

How do you implement specific types on generic traits in rust?

I initially assumed you could do this, because the documentation (http://doc.rust-lang.org/rust.html#implementations) suggests you can:
trait Bar<T> {
fn ex(&self) -> T;
}
struct Foo {
y:f64
}
impl Bar<int> for Foo {
fn ex(&self) -> int {
return self.y.floor() as int;
}
}
impl Bar<uint> for Foo {
fn ex(&self) -> uint {
if (self.y < 0.0) {
return 0u;
}
return self.y.floor() as uint;
}
}
...but that doesn't seem to work. I get errors like:
error: multiple applicable methods in scope
error: expected Bar<uint>, but found Bar<int> (expected uint but found int)
error: expected Bar<int>, but found Bar<uint> (expected int but found uint)
So I figured perhaps Foo has to be generic for this to work, so each specific Foo has it's own Bar implementation on it:
trait Bar<T> {
fn ex(&self) -> T;
}
struct Foo<T> {
y:f64
}
impl<T> Foo<T> {
fn new<U>(value:f64) -> Foo<U> {
return Foo { y: value } as Foo<U>;
}
}
impl Bar<int> for Foo<int> {
fn ex(&self) -> int {
return self.y.floor() as int;
}
}
impl Bar<uint> for Foo<uint> {
fn ex(&self) -> uint {
if (self.y < 0.0) {
return 0u;
}
return self.y.floor() as uint;
}
}
fn main() {
let z = Foo::new::<int>(100.5);
let q = Foo::new::<uint>(101.5);
let i:int = z.ex();
let j:uint = q.ex();
}
...but my constructor seems to not work:
x.rs:11:12: 11:38 error: non-scalar cast: `Foo<<generic #1>>` as `Foo<U>`
x.rs:11 return Foo { y: value } as Foo<U>;
^~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
Edit: I also tried:
impl<T> Foo<T> {
fn new<U>(value:f64) -> Foo<U> {
let rtn:Foo<U> = Foo { y: value };
return rtn;
}
}
Which solve the casting error, but results in:
x.rs:32:11: 32:26 error: cannot determine a type for this expression: unconstrained type
x.rs:32 let z = Foo::new::<int>(100.5);
^~~~~~~~~~~~~~~
O_o I have no idea what that means.
How do you do this?
The impl Bar<int> for Foo and impl Bar<uint> for Foo is an error because, at the moment, only one impl is allowed per trait, type pair (ignoring parameters on the trait). I went into more detail in this answer, including a work around using a secondary trait that avoids having to make Foo generic (which is probably not what you want).
trait BarForFoo {
fn do_ex(foo: &Foo) -> Self;
}
impl BarForFoo for int {
fn do_ex(foo: &Foo) -> int {
foo.y.floor() as int
}
}
impl BarForFoo for uint {
fn do_ex(foo: &Foo) -> uint {
foo.y.max(0.0).floor() as uint
}
}
impl<T: BarForFoo> Bar<T> for Foo {
fn ex(&self) -> T { BarForFoo::do_ex(self) }
}
The second error is because you have two type parameters T and U "in scope" for the new function, but are only specifying one (U). The T needs to be specified by writing Foo::<int>::..., however I don't think this is what you want, instead, you should be using the T generic in the new function:
impl<T> Foo<T> {
fn new(value: f64) -> Foo<T> { ... }
}
As background, the compiler needs to know the concrete type of T because the implementation of new could change:
impl<T> Foo<T> {
fn new<U>(value:f64) -> Foo<U> {
Foo { y: value + std::mem::size_of::<T>() as f64 }
}
}
and then Foo::<()>::new::<int>(0.0) would give y == 0.0, but Foo::<u64>::new::<int>(0.0) would give y == 8.0.

Resources