Rust: polymorphic calls for structs in a vector - rust

I'm a complete newbie in Rust and I'm trying to get some understanding of the basics of the language.
Consider the following trait
trait Function {
fn value(&self, arg: &[f64]) -> f64;
}
and two structs implementing it:
struct Add {}
struct Multiply {}
impl Function for Add {
fn value(&self, arg: &[f64]) -> f64 {
arg[0] + arg[1]
}
}
impl Function for Multiply {
fn value(&self, arg: &[f64]) -> f64 {
arg[0] * arg[1]
}
}
In my main() function I want to group two instances of Add and Multiply in a vector, and then call the value method. The following works:
fn main() {
let x = vec![1.0, 2.0];
let funcs: Vec<&dyn Function> = vec![&Add {}, &Multiply {}];
for f in funcs {
println!("{}", f.value(&x));
}
}
And so does:
fn main() {
let x = vec![1.0, 2.0];
let funcs: Vec<Box<dyn Function>> = vec![Box::new(Add {}), Box::new(Multiply {})];
for f in funcs {
println!("{}", f.value(&x));
}
}
Is there any better / less verbose way? Can I work around wrapping the instances in a Box? What is the takeaway with trait objects in this case?

Is there any better / less verbose way?
There isn't really a way to make this less verbose. Since you are using trait objects, you need to tell the compiler that the vectors's items are dyn Function and not the concrete type. The compiler can't just infer that you meant dyn Function trait objects because there could have been other traits that Add and Multiply both implement.
You can't abstract out the calls to Box::new either. For that to work, you would have to somehow map over a heterogeneous collection, which isn't possible in Rust. However, if you are writing this a lot, you might consider adding helper constructor functions for each concrete impl:
impl Add {
fn new() -> Add {
Add {}
}
fn new_boxed() -> Box<Add> {
Box::new(Add::new())
}
}
It's idiomatic to include a new constructor wherever possible, but it's also common to include alternative convenience constructors.
This makes the construction of the vector a bit less noisy:
let funcs: Vec<Box<dyn Function>> = vec!(Add::new_boxed(), Multiply::new_boxed()));
What is the takeaway with trait objects in this case?
There is always a small performance hit with using dynamic dispatch. If all of your objects are the same type, they can be densely packed in memory, which can be much faster for iteration. In general, I wouldn't worry too much about this unless you are creating a library crate, or if you really want to squeeze out the last nanosecond of performance.

Related

Panic if the capacity of a vector is increased

I am working on implementing a sieve of atkins as my first decently sized program in rust. This algorithm takes a number and returns a vector of all primes below that number. There are two different vectors I need use this function.
BitVec 1 for prime 0 for not prime (flipped back and forth as part of the algorithm).
Vector containing all known primes.
The size of the BitVec is known as soon as the function is called. While the final size of the vector containing all known primes is not known, there are relatively accurate upper limits for the number of primes in a range. Using these I can set the size of the vector to an upper bound then shrink_to_fit it before returning. The upshot of this neither array should ever need to have it's capacity increased while the algorithm is running, and if this happens something has gone horribly wrong with the algorithm.
Therefore, I would like my function to panic if the capacity of either the vector or the bitvec is changed during the running of the function. Is this possible and if so how would I be best off implementing it?
Thanks,
You can assert that the vecs capacity() and len() are different before each push:
assert_ne!(v.capacity(), v.len());
v.push(value);
If you want it done automatically you'd have to wrap your vec in a newtype:
struct FixedSizeVec<T>(Vec<T>);
impl<T> FixedSizeVec<T> {
pub fn push(&mut self, value: T) {
assert_ne!(self.0.len(), self.0.capacity())
self.0.push(value)
}
}
To save on forwarding unchanged methods you can impl Deref(Mut) for your newtype.
use std::ops::{Deref, DerefMut};
impl<T> Deref for FixedSizeVec<T> {
type Target = Vec<T>;
fn deref(&self) -> &Vec<T> {
&self.0
}
}
impl<T> DerefMut for FixedSizeVec<T> {
fn deref_mut(&mut self) -> &mut Vec<T> {
&mut self.0
}
}
An alternative to the newtype pattern is to create a new trait with a method that performs the check, and implement it for the vector like so:
trait PushCheck<T> {
fn push_with_check(&mut self, value: T);
}
impl<T> PushCheck<T> for std::vec::Vec<T> {
fn push_with_check(&mut self, value: T) {
let prev_capacity = self.capacity();
self.push(value);
assert!(prev_capacity == self.capacity());
}
}
fn main() {
let mut v = Vec::new();
v.reserve(4);
dbg!(v.capacity());
v.push_with_check(1);
v.push_with_check(1);
v.push_with_check(1);
v.push_with_check(1);
// This push will panic
v.push_with_check(1);
}
The upside is that you aren't creating a new type, but the obvious downside is you need to remember to use the newly defined method.

Casting unknown type to know type

So I have this function
fn render_i32(n: &dyn Typeable, echo: &dyn Fn(&String)) {
let x: &i32 = unsafe {transmute(n)};
echo(&x.to_string());
}
It does not compile because cannot transmute between types of different sizes.
What I want with this code is the following: I have a HashMap which contains rendering functions for different types. Every type that may be rendered must implement my interface Typeable, which basically only returns a constant type_id for the type (I've just come across a type_id in std, and wonder if I could use that instead...). And using that type_id I can then look up the correct render function in my HashMap. So my code ensures, that render_i32 is only called for i32. This works fine.
Now all of this would be really easy in C where I'd just cast the value under the pointer. But in rust it does not appear to be so easy. I don't get at the i32 value. How would I get that?
Edit: Alternative Solutions to my own approach that are less type-unsafe but solve the following requirement are also welcome: clients (who use this library) should be able to add their own rendering functions for their own types...
Note that the rendering functions are not supposed to be statically defined once: different rendering functions might be used for the same type depending for example on a language setting.
I still don't get why you didn't use the conventional trait-impl approach, it seems to do what you wanted, except that function pointers don't have any common data structure holding them (it's probably less cache-friendly than HashMap's approach)
Playground
use std::iter;
// lib
fn echo_windows(s: &String) {
println!("C:/Users> {}", s)
}
fn echo_linux(s: &String) {
println!("$ {}", s)
}
trait Renderable {
fn render(&self, echo: &dyn Fn(&String));
}
// client
struct ClientType {
ch: char,
len: usize,
}
impl Renderable for ClientType {
fn render(&self, echo: &dyn Fn(&String)) {
let to_echo: String = iter::repeat(self.ch)
.take(self.len)
.collect();
echo(&to_echo);
}
}
fn main() {
ClientType{ ch: '#', len: 5 }.render(&echo_windows); // output: C:/Users> #####
ClientType{ ch: '!', len: 3 }.render(&echo_linux); // output: $ !!!
}
Maybe you can use the Any trait for your purpose:
use std::any::Any;
pub trait Typeable {
...
fn as_any(&self) -> &dyn Any;
}
fn render_i32(n: &dyn Typeable, echo: &dyn Fn(&String)) {
let x: &i32 = n.as_any().downcast_ref::<i32>().unwrap();
echo(&x.to_string());
}
The downcast_ref::<i32>() method returns an Option<&i32>, so you can also check if the downcast is valid. You can even do this in a generic way:
fn render<T:'static + std::fmt::Display>(n: &dyn Typeable, echo: &dyn Fn(&String)) {
let x: &T = n.as_any().downcast_ref::<T>().unwrap();
echo(&x.to_string());
}

structs with boxed vs. unboxed closures

I'm still internalizing closures in Rust and how to best work with them, so this question might be somewhat vague, and there will perhaps be silly sub-questions. I'm basically looking for proper idioms and maybe even transforming the way I think about how to do some stuff in Rust.
Storing unboxed closure
The Rust book has an example simple Cacher in it's chapter on closures:
struct Cacher<T>
where
T: Fn(u32) -> u32,
{
calculation: T,
value: Option<u32>,
}
impl<T> Cacher<T>
where
T: Fn(u32) -> u32,
{
fn new(calculation: T) -> Cacher<T> {
Cacher {
calculation,
value: None,
}
}
fn value(&mut self, arg: u32) -> u32 {
match self.value {
Some(v) => v,
None => {
let v = (self.calculation)(arg);
self.value = Some(v);
v
}
}
}
}
It is supposed to be used like this:
let mut c = Cacher::new(|a| a);
let v1 = c.value(1);
That is perfectly fine and useful, but what if I need to have this Cacher be a member of another struct, say (in the spirit of the Rust book chapter), a WorkoutFactory? Since Cacher is parameterized by the closure's type, I am forced to parameterize WorkoutFactory with the same closure type.
Is my understanding correct? I guess so, the Cacher struct structure depends on the type T of the calculation, so the struct WorkoutFactory structure must depend on the type of Cacher. On one hand this feels like a natural, unavoidable and perfectly justified consequence of how closures work in Rust, on the other hand it means that
WorkoutFactory can be contained in another struct that is also forced to be parameterized by T, which can be contained in another struct, ... - the closure type spreads like plague. With perhaps other T's coming from deep down the member hierarchy, the signature of the top-level struct can become monstrous.
the fact that there is some caching involved in WorkoutFactory should be just an implementation detail, perhaps the caching was even added at version 2.0, but the type parameter is visible in the public interface of WorkoutFactory and needs to be accounted for. The seemingly implementation detail is now part of the interface, no good :(
Is there some way to work around these problems without changing the signature of Cacher? How do others cope with this?
Storing boxed closure
If I want to get rid of the type parameters, I can Box the closure. I've come up with the following code:
struct BCacher {
calculation: Box<Fn(u32) -> u32>,
value: Option<u32>,
}
impl BCacher {
fn new<T: Fn(u32) -> u32 + 'static>(calculation: T) -> BCacher {
BCacher {
calculation: Box::new(calculation),
value: None,
}
}
fn value(&mut self, arg: u32) -> u32 {
match self.value {
Some(v) => v,
None => {
let v = (self.calculation)(arg);
self.value = Some(v);
v
}
}
}
}
I can use it exactly like Cacher:
let mut c = BCacher::new(|a| a);
let v1 = c.value(1);
...almost :( The 'static' annotation means I can't do this:
let x = 1;
let mut c = BCacher::new(|a| a + x);
because the closure may outlive x. That is unfortunate, something possible with the non-boxed version is no longer possible with the boxed version.
Furthermore, this version is less efficient, it is necessary to dereference the Box (is that correct?), and RAM access is slow. The difference will most probably be negligible in most cases, but still..
I could address the first issue with lifetime annotation:
struct BLCacher<'a> {
calculation: Box<Fn(u32) -> u32 + 'a>,
value: Option<u32>,
}
but now I'm back to Cacher with type parameters and all the unpleasant consequences of that.
The right to choose
This seems like an unfortunate situation. I have two approaches to storing closure in a struct, and each has it's own set of problems. Let's say I'm willing to live with that, and as the author of the awesome fictional Cacher crate, I want to present the users with both implementations of Cacher, the unboxed Cacher and the boxed BCacher. But I don't want to write the implementation twice. What would be the best way - if there's any at all - to use an existing Cacher implementation to implement BCacher?
On a related note (maybe it is even the same question), let's assume I have a
struct WorkoutFactory<T>
where
T: Fn(u32) -> u32,
{
cacher: Cacher<T>,
}
Is there a way to implement GymFactory without type parameters that would contain - for private purposes - WorkoutFactory with type parameters, probably stored in a Box?
Summary
A long question, sorry for that. Coming from Scala, working with closures is a LOT less straightforward in Rust. I hope I've explained the struggles I've not yet found satisfactory answers to.
Not a full answer sorry, but in this bit of code here:
let x = 1;
let mut c = BCacher::new(|a| a + x);
Why not change it to:
let x = 1;
let mut c = BCacher::new(move |a| a + x);
That way the functor will absorb and own 'x', so there won't be any reference to it, which should hopefully clear up all the other issues.

Is there any downside to overloading functions in rust using a trait & generic function?

I find this is particularly useful pattern to allow method overloading:
struct Foo {
value:uint
}
trait HasUIntValue {
fn as_uint(self) -> uint;
}
impl Foo {
fn add<T:HasUIntValue>(&mut self, value:T) {
self.value += value.as_uint();
}
}
impl HasUIntValue for int {
fn as_uint(self) -> uint {
return self as uint;
}
}
impl HasUIntValue for f64 {
fn as_uint(self) -> uint {
return self as uint;
}
}
#[test]
fn test_add_with_int()
{
let mut x = Foo { value: 10 };
x.add(10i);
assert!(x.value == 20);
}
#[test]
fn test_add_with_float()
{
let mut x = Foo { value: 10 };
x.add(10.0f64);
assert!(x.value == 20);
}
Is there any meaningful downside to doing this?
There is at least one downside: it cannot be an afterthought.
In C++, ad-hoc overloading allows you to overload a function over which you have no control (think 3rd party), whereas in Rust this is not actually doable.
That being said, ad-hoc overloading is mostly useful in C++ because of ad-hoc templates, which is the only place where you cannot know in advance which function the call will ultimately resolve to. In Rust, since templates are bound by traits, the fact that overloading cannot be an afterthought is not an issue since only the traits functions can be called anyway.
No, there is no downside; this is exactly the pattern to implement overloading in Rust.
There are a number of types in the standard library which do exactly this. For example, there is BytesContainer trait in path module, which is implemented for various kinds of strings and vectors.

How to take ownership of Any:downcast_ref from trait object?

I've met a conflict with Rust's ownership rules and a trait object downcast. This is a sample:
use std::any::Any;
trait Node{
fn gen(&self) -> Box<Node>;
}
struct TextNode;
impl Node for TextNode{
fn gen(&self) -> Box<Node>{
Box::new(TextNode)
}
}
fn main(){
let mut v: Vec<TextNode> = Vec::new();
let node = TextNode.gen();
let foo = &node as &Any;
match foo.downcast_ref::<TextNode>(){
Some(n) => {
v.push(*n);
},
None => ()
};
}
The TextNode::gen method has to return Box<Node> instead of Box<TextNode>, so I have to downcast it to Box<TextNode>.
Any::downcast_ref's return value is Option<&T>, so I can't take ownership of the downcast result and push it to v.
====edit=====
As I am not good at English, my question is vague.
I am implementing (copying may be more precise) the template parser in Go standard library.
What I really need is a vector, Vec<Box<Node>> or Vec<Box<Any>>, which can contain TextNode, NumberNode, ActionNode, any type of node that implements the trait Node can be pushed into it.
Every node type needs to implement the copy method, return Box<Any>, and then downcasting to the concrete type is OK. But to copy Vec<Box<Any>>, as you don't know the concrete type of every element, you have to check one by one, that is really inefficient.
If the copy method returns Box<Node>, then copying Vec<Box<Node>> is simple. But it seems that there is no way to get the concrete type from trait object.
If you control trait Node you can have it return a Box<Any> and use the Box::downcast method
It would look like this:
use std::any::Any;
trait Node {
fn gen(&self) -> Box<Any>; // downcast works on Box<Any>
}
struct TextNode;
impl Node for TextNode {
fn gen(&self) -> Box<Any> {
Box::new(TextNode)
}
}
fn main() {
let mut v: Vec<TextNode> = Vec::new();
let node = TextNode.gen();
if let Ok(n) = node.downcast::<TextNode>() {
v.push(*n);
}
}
Generally speaking, you should not jump to using Any. I know it looks familiar when coming from a language with subtype polymorphism and want to recreate a hierarchy of types with some root type (like in this case: you're trying to recreate the TextNode is a Node relationship and create a Vec of Nodes). I did it too and so did many others: I bet the number of SO questions on Any outnumbers the times Any is actually used on crates.io.
While Any does have its uses, in Rust it has alternatives.
In case you have not looked at them, I wanted to make sure you considered doing this with:
enums
Given different Node types you can express the "a Node is any of these types" relationship with an enum:
struct TextNode;
struct XmlNode;
struct HtmlNode;
enum Node {
Text(TextNode),
Xml(XmlNode),
Html(HtmlNode),
}
With that you can put them all in one Vec and do different things depending on the variant, without downcasting:
let v: Vec<Node> = vec![
Node::Text(TextNode),
Node::Xml(XmlNode),
Node::Html(HtmlNode)];
for n in &v {
match n {
&Node::Text(_) => println!("TextNode"),
&Node::Xml(_) => println!("XmlNode"),
&Node::Html(_) => println!("HtmlNode"),
}
}
playground
adding a variant means potentially changing your code in many places: the enum itself and all the functions that do something with the enum (to add the logic for the new variant). But then again, with Any it's mostly the same, all those functions might need to add the downcast to the new variant.
Trait objects (not Any)
You can try putting the actions you'd want to perform on the various types of nodes in the trait, so you don't need to downcast, but just call methods on the trait object.
This is essentially what you were doing, except putting the method on the Node trait instead of downcasting.
playground
The (more) ideomatic way for the problem:
use std::any::Any;
pub trait Nodeable {
fn as_any(&self) -> &dyn Any;
}
#[derive(Clone, Debug)]
struct TextNode {}
impl Nodeable for TextNode {
fn as_any(&self) -> &dyn Any {
self
}
}
fn main() {
let mut v: Vec<Box<dyn Nodeable>> = Vec::new();
let node = TextNode {}; // or impl TextNode::new
v.push(Box::new(node));
// the downcast back to TextNode could be solved like this:
if let Some(b) = v.pop() { // only if we have a node…
let n = (*b).as_any().downcast_ref::<TextNode>().unwrap(); // this is secure *)
println!("{:?}", n);
};
}
*) This is secure: only Nodeables are allowd to be downcasted to types that had Nodeable implemented.

Resources