Generic enum with several parameters?

What is proper way of declaring generic enum with several parameters?

use std::rc::Rc;

//

fn main()
{

  let p1 = Expr::val( true );
  println!( "p1 : {:?}", p1 );

  let p2 = Expr::other( true );
  println!( "p2 : {:?}", p2 );

}

//

#[derive( Debug )]
pub enum Expr< T = (), V : Copy = () >
{
  Val( V ),
  Other( Rc< T > ),
}

//

impl< T, V : Copy > Expr< T, V >
{
  pub fn val( val : V ) -> Self
  {
    Expr::Val( val )
  }

  pub fn other( p : T ) -> Self
  {
    Expr::Other( Rc::new( p ) )
  }

}

Why cant it deduce types?

error[E0282]: type annotations needed for `Expr<T, bool>`
 --> src/main.rs:8:12
  |
8 |   let p1 = Expr::val( true );
  |       --   ^^^^^^^^^ cannot infer type for type parameter `T`
  |       |
  |       consider giving `p1` the explicit type `Expr<T, bool>`, where the type parameter `T` is specified

For more information about this error, try `rustc --explain E0282`.
error: could not compile `playground` due to previous error

Playground is here.

Any reading to overcome my lack of understanding?

Seems I have found:

use std::rc::Rc;

//

fn main()
{

  let p1 = Expr::val( true );
  println!( "p1 : {:?}", p1 );

  let p2 = Expr::other( true );
  println!( "p2 : {:?}", p2 );

}

//

#[derive( Debug )]
pub enum Expr< T = (), V : Copy = () >
{
  Val( V ),
  Other( Rc< T > ),
}

//

impl Expr
{

  pub fn val< V : Copy >( val : V ) -> Expr::< (), V >
  {
    Expr::< (), V >::Val( val )
  }

  pub fn other< T >( p : T ) -> Expr::< T, () >
  {
    Expr::Other( Rc::new( p ) )
  }

}

Is there better solution?

If Expr::var() always returns Expr<(), T>, code like this will not compile:

    let mut p1 = Expr::val(true);  // compiler infers V = bool, but T is unknown
    println!("p1 : {:?}", p1);

    let p2 = Expr::other(true);  // compiler infers T = bool, but V is unknown
    println!("p2 : {:?}", p2);

    // you assign p2 to p1, so p1 and p2 must be the same type, now compler
    // can figure out that it must be Expr<boo, bool>
    p1 = p2;  

this code compiles in your first solution.

1 Like

But that does not :frowning:

use std::rc::Rc;

//

fn main()
{

  let p1 = Expr::val( true );
  println!( "p1 : {:?}", p1 );

  // let p2 = Expr::other( true );
  // println!( "p2 : {:?}", p2 );

  // let p3 = Expr::< (), bool >::Val( true );
  // println!( "p2 : {:?}", p3 );

}

//

#[derive( Debug )]
pub enum Expr< T = (), V : Copy = () >
{
  Val( V ),
  Other( Rc< T > ),
}

//

impl< T, V : Copy > Expr< T, V >
{

  pub fn val( val : V ) -> Expr::< (), V >
  {
    Expr::Val( val )
  }

  // pub fn other< T >( p : T ) -> Expr::< T, () >
  // {
  //   Expr::Other( Rc::new( p ) )
  // }

}

Does it? :roll_eyes:

That does not.

Okay, so solution is:

use std::rc::Rc;

//

fn main()
{

  let p1 = Expr::val( true );
  println!( "p1 : {:?}", p1 );

  let p2 = Expr::other( true );
  println!( "p2 : {:?}", p2 );

}

//

#[derive( Debug )]
pub enum Expr< T = (), V : Copy = () >
{
  Val( V ),
  Other( Rc< T > ),
}

//

impl Expr
{

  pub fn val< V : Copy >( val : V ) -> Expr::< (), V >
  {
    Expr::< (), V >::Val( val )
  }

  pub fn other< T >( p : T ) -> Expr::< T, () >
  {
    Expr::Other( Rc::new( p ) )
  }

}

But this impl Expr only for constructors and static functions. Methods should be implemented in impl< T, V : Copy > Expr< T, V >. Like that:

impl< T, V : Copy > Expr< T, V >
{

// ... methods ...

}

Rust allows having several impl.

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.