You can have more control over code generation by using abstract data types.
pub mod stack {
pub struct Stack<T> {
v: Vec<T>
}
impl<T> Stack<T> {
pub fn new() -> Self {
Stack{v: Vec::new()}
}
pub fn push(&mut self, x: T) {
self.v.push(x);
}
pub fn pop(&mut self) -> Option<T> {
self.v.pop()
}
}
}
pub mod stack_polymorphic {
use std::any::Any;
use std::marker::PhantomData;
pub struct Stack<T> {
v: Vec<Box<dyn Any>>,
_marker: PhantomData<T>
}
impl<T: 'static> Stack<T> {
pub fn new() -> Self {
Self{v: Vec::new(), _marker: PhantomData}
}
pub fn push(&mut self, x: T) {
self.v.push(Box::new(x));
}
pub fn pop(&mut self) -> Option<T> {
match self.v.pop() {
None => None,
Some(x) => {
if let Ok(x) = x.downcast::<T>() {
Some(*x)
}else{
unreachable!()
}
}
}
}
}
}
type StackADT<T> = stack_polymorphic::Stack<T>;
fn main() {
let mut a: StackADT<i32> = StackADT::new();
a.push(1);
a.push(2);
println!("{:?}",a.pop());
println!("{:?}",a.pop());
}
One might define more variants:
type FastStackADT<T> = stack::Stack<T>;
type MediumStackADT<T> = stack::Stack<T>;
type SmallStackADT<T> = stack_polymorphic::Stack<T>;