Home > Back-end >  Comparing Rust Traits with C# interface
Comparing Rust Traits with C# interface

Time:09-18

I am trying to better understand Rust Generic Associate Type by comparing Rust Traits to C# interfaces.

In Rust we can define Iterator trait with GATs such as

trait iterator
{
   type Output;
   fn next(&mut self) -> Option<Self::Item>;
   ...
}

Which I believe is similar to C# interface where the associated type is defined via the out paramter

public interface IEnumerable<out T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();
}

Q1 - are the two definitions comparable?

Q2 - what is the difference between Rust Iterator defined above with GAT vs compared to below?

trait NewIterate<T>
{
    fn next(&mut self) -> Option<T>;
}

Q3 In C# I can define a self referencing interface like so

interface IMultiply<O, R>{
    R multiply(IMultiply<O,R> rhs);
}

How do i do the same in Rust with traits? I tried below but both returned "cannot find MyMultiply in scope"

trait MyMuliply<T, O>{
    fn mul(&self, rhs: impl MyMultiply) -> O;
}


trait MyMuliply<T, O>{
    fn mul(&self, rhs: MyMultiply) -> O;
}

Q4 Taking Example from Orielly's Programming in Rust book, we cannot do below in Rust because the Size of both Circle and Triangle are different

trait Shape{
   fn new() -> Self;
   fn area(&self) -> f64;
}

/* we cannot do like this */

fn make_shape(shape: &str) -> impl Shape{
  match shape{
     "circle" => Circle::new(),
     "triangle" => Triangle::new() // error: incompatible types
  }
}

Yet in C# i can do something like below

public interface IShape{
    public IShape Construct();
    public Double Area();
}

public class Circle:IShape{
    public IShape Construct(){
        return new Circle();    
    }
    
    public Double Area(){
        return 100; 
    }
}

public class Triangle:IShape{
    public IShape Construct(){
        return new Triangle();  
    }
    
    public Double Area(){
        return 50;  
    }
}

public class Program
{
    IShape MakeShape(String str){
        if(str.Equals("circle")){
            return new Circle();    
        }
        
        return new Triangle();
    }
}

I am pretty sure I am missing very basic here as i would think Rust would be better at type system, please if someone can correct me?

Edit - What helped me was this answer Why don't associated types for protocols use generic type syntax in Swift?. Guess i was asking for difference between Associated Type vs Generic, the difference is that with generic types Callee drives the type info, while with assoicated type it is the type (Napier's example of Array is what helped me clarify it for me)

CodePudding user response:

  1. yeah, they're pretty similar, though rust's Iterator is closer to c#'s IEnumerator (i.e. it is the thing that you can actually iterator over), IEnumerable is closer to IntoIterator in Rust

  2. This is the difference between generics and associated types (nothing to do with GATs)

Iterator can only be implemented once for a particular type. But a single type could implement NewIterator<String> and NewIterator<i32>.

  1. This is generally possible, you just have a typo in the name MyMultiply. You can't use a trait as a raw parameter though, it must be some sort of generic/trait object

  2. impl Trait when in return position is a single type implementing that trait, hence you can't return different types based on runtime information. You're probably looking for either:

  • an enum, if you know all the possible variants at compile time
  • a trait object (i.e. returning Box<dyn Trait>) if you really want to have multiple different types returned from one function

A regular Rust type carries no type information at runtime, only trait objects do, but they suffer the performance cost of:

  • a possible heap allocation, depending on how you use them
  • a vtable lookup for each function call, which significantly hinders the optimizer
  • Related