-1

I'm building a wrapper around a DLL. This DLL gives me access to a database engine which implements an OOP design pattern. This requires me to create multiple overlapping traits that cover all the functionality:

pub trait CursorStatement { /* ... */ }
pub trait CursorTable { /* ... */ }
pub trait CursorStatementTable { /* ... */ }
...

I want to be able to bring these traits in scope so that I can call the functions without having to list every trait. Right now I'm doing:

mod traittest;
use traittest::*;

fn test() -> Result<(), AceError> {
    let t = traittest::Table::new(3, "ORDERS")?;
    let c = traittest::Cursor { handle: 42 };
    println!("t.fields={}", t.fields());
    println!("c.fields={}", c.fields());
    Ok(())
}

fn main() {
    test().expect("success");
}

The problem with use foo::* is that it puts everything from the module into my namespace, which I don't want.

In the example above, I don't have to type traittest::Table or traittest::Cursor, I just have to type Table or Cursor. However, I want to have to prefix those objects with the module name so when I'm reading the code I know where the objects came from. I might want to create a Table object in my local file that is distinguished from the one coming from the module.

I also don't want to have to do the following because if I later have to add a new trait I will have to update a bunch of other source files that depend on this module:

mod traittest; 
use traittest::{CursorStatement, CursorStatementTable, CursorTable, /* ... */};

I tried creating a Traits supertrait that would inherit all other traits as shown in Is there any way to create a type alias for multiple traits?, but it doesn't work because I can't implement the trait for anything because there's nothing that would be an implementation of every trait in the file:

pub trait Traits: CursorStatement, CursorTable, CursorStatementHandle, /* ... */ {}

If I could create a named scope for all the traits, that would work, but I can't figure out how to make Rust happy with this idea:

let traits = {
    pub trait CursorTable { /* ... */ }
}

It looks like this trait_group macro might do the trick but it's not obvious to me how I could use it to solve my problem.

Here's my entire program

mod traittest {
    #[derive(Debug)]
    pub struct AceError {
        code: u32,
        description: String,
    }

    pub trait CursorTable {
        fn get_handle(&self) -> u32; // impl's must write this function
        fn fields(&self) -> String {
            return format!("(get_handle() -> {})", self.get_handle());
        }
    }

    pub struct Table {
        pub handle: u32,
        pub table_name: String,
    }

    pub struct Cursor {
        pub handle: u32,
    }

    impl Table {
        pub fn new(handle: u32, table_name: &str) -> Result<Table, AceError> {
            let table = Table {
                handle: handle,
                table_name: table_name.to_string(),
            };
            return Ok(table);
        }
    }

    impl CursorTable for Table {
        fn get_handle(&self) -> u32 {
            return self.handle;
        }
    }

    impl CursorTable for Cursor {
        fn get_handle(&self) -> u32 {
            return self.handle;
        }
    }

    pub trait Traits: CursorTable {} /* super trait to bring all other traits in scope */
}

use traittest::Traits;

fn test() -> Result<(), traittest::AceError> {
    let t = traittest::Table::new(3, "ORDERS")?;
    let c = traittest::Cursor { handle: 42 };
    println!("t.fields={}", t.fields());
    println!("c.fields={}", c.fields());
    Ok(())
}

fn main() {
    test().expect("success");
}

and here's the error I get:

warning: unused import: `traittest::Traits`
  --> src/main.rs:49:5
   |
49 | use traittest::Traits;
   |     ^^^^^^^^^^^^^^^^^
   |
   = note: `#[warn(unused_imports)]` on by default

error[E0599]: no method named `fields` found for struct `traittest::Table` in the current scope
  --> src/main.rs:54:31
   |
10 |         fn fields(&self) -> String {
   |            ------
   |            |
   |            the method is available for `std::boxed::Box<traittest::Table>` here
   |            the method is available for `std::sync::Arc<traittest::Table>` here
   |            the method is available for `std::rc::Rc<traittest::Table>` here
...
15 |     pub struct Table {
   |     ---------------- method `fields` not found for this
...
54 |     println!("t.fields={}", t.fields());
   |                               ^^^^^^ method not found in `traittest::Table`
   |
   = help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
   |
49 | use crate::traittest::CursorTable;
   |

error[E0599]: no method named `fields` found for struct `traittest::Cursor` in the current scope
  --> src/main.rs:55:31
   |
10 |         fn fields(&self) -> String {
   |            ------
   |            |
   |            the method is available for `std::boxed::Box<traittest::Cursor>` here
   |            the method is available for `std::sync::Arc<traittest::Cursor>` here
   |            the method is available for `std::rc::Rc<traittest::Cursor>` here
...
20 |     pub struct Cursor {
   |     ----------------- method `fields` not found for this
...
55 |     println!("c.fields={}", c.fields());
   |                               ^^^^^^ method not found in `traittest::Cursor`
   |
   = help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
   |
49 | use crate::traittest::CursorTable;
   |
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
royce3
  • 1,203
  • 1
  • 10
  • 17

1 Answers1

-2

I finally figured out a solution I can live with, in case anybody else is looking for a solution to this problem.

In your module where you define your traits, create a sub-module with all the traits, like so:

pub mod Traits
{
    pub trait CursorTrait
    {
        fn get_handle ( &self ) -> u32; // impl's must write this function
        fn fields ( &self ) -> String
        {
            return format! ( "(get_handle() -> {})", self.get_handle() );
        }
    }
}

Now in your other modules, if you want to bring the traits in scope without bringing in the entire module, you can just bring in the submodule, like so:

mod foo; use foo::Traits::*;
royce3
  • 1,203
  • 1
  • 10
  • 17
  • 3
    This is the *prelude pattern*. See [`std::prelude`](https://doc.rust-lang.org/std/prelude/index.html), [`std::io::prelude`](https://doc.rust-lang.org/std/io/prelude/index.html), etc. – Shepmaster Jul 20 '20 at 17:05
  • If I understand this correctly, to do this the Rustonic way I should name it "prelude" instead of "traits"? – royce3 Jul 20 '20 at 17:11
  • 2
    Why did you rollback my edit? I was just applying standard rust formatting and naming. – John Kugelman Jul 20 '20 at 17:13
  • I didn't like the changes, I prefer it the way I wrote it, thanks. – royce3 Jul 20 '20 at 17:16
  • 2
    @royce3 this answer is somehow intended to be usable for people new to rust. When writing such an answer it's a good idea to walk the extra step to make it readable and enlightening to those people too, not just you. It's my opinion that the edit was helpful in that regard. – Denys Séguret Jul 20 '20 at 17:45
  • 1
    "required me to look at why my post was being edited" yes, you can or in fact everyone can see what the edit have done. Do you know how to use tool of SO https://stackoverflow.com/posts/63000329/revisions, it is clear what changed. Sorry but on SO you should accept people edit your answers, also, your answer will be more read than edited so please stop format your code like this nobody like that. – Stargateur Jul 20 '20 at 18:15
  • 1
    The downvotes do seem excessive here and I hope they're not just related to the non orthodox style – Denys Séguret Jul 20 '20 at 18:22
  • You can clearly see that the code is the same with the exception of whitespace and capitalization, in both the "inline" and "side-by-side" views of the revisions. There was no reason to rollback either edit. – Herohtar Jul 20 '20 at 18:42
  • I didn't mean to start a pile-on. I'd undo my vote if I could but I SO won't let me without the answer being edited. – John Kugelman Jul 20 '20 at 18:50