I've recently taken on learning Rust and I'm trying to write a small expression evaluator. I've been practicing Rust for a few days now and thought this task would be cool to work with Rust's Traits. What I tried to do is make Sum & Number structs implement Expression trait, so that I could express (pun unintended) (1 + 2) as an expression where left and right hand sides are expressions too.
I've stumbled onto the problem that you can't just use Traits as properties' types, so you instead should use &dyn Trait or Box in the Book. Following this notion a rewrote it and now it compiles, but I can't get access to values inside Sum.
Here's my code:
trait Expression {}
#[derive(Debug)]
struct Number {
pub val: i32
}
impl Expression for Number {}
struct Sum {
pub left: Box<dyn Expression>,
pub right: Box<dyn Expression>
}
impl Expression for Sum {}
fn main() {
let e = Sum{ left: Box::new(Number{ val: 2}),
right: Box::new(Number{ val: 2})
};
let sum = (2 + 2);
println!("{:#?}", sum);
}
What I want to be able to do is get to Number's value:
e.left.val
and use nested constuctions like:
Sum{Sum{Number, Sum{Number, Number}}, Number}
I also tried to make explicit cast to Number:
let val = (e.left as Number).val;
But it fails with an error:
non-primitive cast: std::boxed::Box<(dyn Expression + 'static)> as Number
note: an as expression can only be used to convert between primitive types. Consider using the From trait.
Sorry for any language mistakes or messy explanation, English is not my first language.
I'm not an experienced programmer and very new to Rust so I would really appreciate any help, thanks!
Rust doesn't let you cast non primitive types.
Reference:
https://doc.rust-lang.org/rust-by-example/types/cast.html
I think what you're trying to do is this (complete code in the playground):
trait Expression {
fn evaluate(&self) -> i32;
}
impl Expression for Number {
fn evaluate(&self) -> i32 {
self.val
}
}
impl Expression for Sum {
fn evaluate(&self) -> i32 {
self.left.evaluate() + self.right.evaluate()
}
}
Related
I was experimenting with const generics when this strange error came out: error: unconstrained generic constant. What does it mean?
Maybe I should describe what was I tried to do before actual code - I wanted to treat types as numbers, using Lisp-like Cons and Nil.
Here's an example:
use core::marker::PhantomData;
struct Nil;
struct Cons <N> (PhantomData <N>);
So '1' is Cons <Nil>, '2' - Cons <Cons <Nil>>, etc.
Then I tried to implement extracting normal number from this.
trait Len {
const N: usize;
}
impl <N: Len> Len for Cons <N> {
const N: usize = 1 + N::N;
}
impl Len for Nil {
const N: usize = 0;
}
And it works.
Then I started doing actual job: my true task was not to just experiment with generic types, but to implement mathematical vecs, just like(maybe you know) in shaders.
So I tried to implement such powerful constructor:
vec3 i = vec3(1.0, 1.0, 1);
vec4 v = vec4(i);
I have decided to treat any value as 'Piece' and then just compound pieces together.
trait Piece <T> {
type Size: Len;
fn construct(self) -> [T; Self::Size::N];
}
No problems so far. Next step is to define few auxiliary traits(since rust does not yet support negative bounds):
pub auto trait NotTuple {}
impl <T> !NotTuple for (T,) {}
pub auto trait NotList {}
impl <T, const N: usize> !NotList for [T; N] {}
I am using nightly rust, so few #![feature]-s is not a problem.
Then, we can use it:
type One = Cons <Nil>;
impl <T: Copy + From <U>, U: Copy + NotTuple + NotList> Piece <T> for U {
type Size = One;
fn construct(self) -> [T; Self::Size::N] {
[T::from(self)]
}
}
This one constructs piece from an argument.
Everything is still good.
impl <T: Copy, U: Piece <T>> Piece <T> for (U,) {
type Size = U::Size;
fn construct(self) -> [T; Self::Size::N] {
self.0.construct()
}
}
And this is where problem occurs. Compiler says that Self::Size::N is an unconstrained generic constant, and tries to help with this: try adding a 'where' bound using this expression: 'where [(); Self::Size::N]:', but this is just useless.
Can anyone explain me what is going on and how to fix this issue?
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());
}
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.
I want to convert a usize typed variable into a u32 typed variable in Rust. I am aware that the usize variable might contain a value larger than 2^32, and in that case the conversion should fail. I am trying to use the TryFrom trait to perform the conversion.
This is a simple example (Nightly Rust, Playground):
#![feature(try_from)]
use std::convert::TryFrom;
fn main() {
let a: usize = 0x100;
let res = u32::try_from(a);
println!("res = {:?}", res);
}
The code doesn't compile, with the following compilation error:
error[E0277]: the trait bound `u32: std::convert::From<usize>` is not satisfied
--> src/main.rs:6:15
|
6 | let res = u32::try_from(a);
| ^^^^^^^^^^^^^ the trait `std::convert::From<usize>` is not implemented for `u32`
|
= help: the following implementations were found:
<u32 as std::convert::From<std::net::Ipv4Addr>>
<u32 as std::convert::From<u8>>
<u32 as std::convert::From<char>>
<u32 as std::convert::From<u16>>
= note: required because of the requirements on the impl of `std::convert::TryFrom<usize>` for `u32`
I deduce from the compilation error that having TryFrom<usize> for u32 is dependent on having From<usize> for u32, which seems somewhat strange to me.
Is there any other way I could utilize TryFrom to convert from usize to u32? If not, is there any other idiomatic way to perform this conversion?
I know that I can use the as keyword, but it doesn't notify me if something went wrong with the conversion. In addition, I think that I can write my own function that does the conversion, but I would be surprised if Rust doesn't have some idiomatic way to do this conversion. usize and u32 are two basic types, after all.
Since this answer was created, it was decided to have the implementation of TryFrom<usize> always allow for the possibility of failure, regardless of the current platform. The original code now compiles successfully in Rust 1.34.
Original answer
having TryFrom<usize> for u32 is dependent on having From<usize> for u32, which seems somewhat strange to me
This is because there's a blanket implementation of TryFrom for anything that implements From:
impl<T, U> TryFrom<U> for T
where
T: From<U>,
{
type Error = !;
}
As you mentioned, since Rust supports platforms where the native integer length is 16, 32, or 64 bits, having such an implementation of From / Into would not be lossless on some of these platforms.
This error occurs because there's no direct implementation of TryFrom / TryInto for these types. This is because users of these traits prefer that the implementations be infallible when platform-appropriate (The type Error = !).
There is a separate tracking issue 49415 specifically for deciding this issue.
I think that I can write my own function that does the conversion
Yes, that is what you should do. Something like this untested piece of code:
use std::u32;
struct SomeError;
// usize is a u16 or u32, which always fits in a u32
#[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
fn my_thing(a: usize) -> Result<u32, SomeError> {
Ok(a as u32)
}
// usize is a u64, which might be too big
#[cfg(target_pointer_width = "64")]
fn my_thing(a: usize) -> Result<u32, SomeError> {
if a > u32::MAX as usize {
Err(SomeError)
} else {
Ok(a as u32)
}
}
I would be surprised if Rust doesn't have some idiomatic way to do this conversion. usize and u32 are two basic types, after all.
The problem is that usize isn't really a "basic" type because it changes size depending on the target platform. Getting this correct, performant and ergonomic is not easy.
I have a program that involves examining a complex data structure to see if it has any defects. (It's quite complicated, so I'm posting example code.) All of the checks are unrelated to each other, and will all have their own modules and tests.
More importantly, each check has its own error type that contains different information about how the check failed for each number. I'm doing it this way instead of just returning an error string so I can test the errors (it's why Error relies on PartialEq).
My Code So Far
I have traits for Check and Error:
trait Check {
type Error;
fn check_number(&self, number: i32) -> Option<Self::Error>;
}
trait Error: std::fmt::Debug + PartialEq {
fn description(&self) -> String;
}
And two example checks, with their error structs. In this example, I want to show errors if a number is negative or even:
#[derive(PartialEq, Debug)]
struct EvenError {
number: i32,
}
struct EvenCheck;
impl Check for EvenCheck {
type Error = EvenError;
fn check_number(&self, number: i32) -> Option<EvenError> {
if number < 0 {
Some(EvenError { number: number })
} else {
None
}
}
}
impl Error for EvenError {
fn description(&self) -> String {
format!("{} is even", self.number)
}
}
#[derive(PartialEq, Debug)]
struct NegativeError {
number: i32,
}
struct NegativeCheck;
impl Check for NegativeCheck {
type Error = NegativeError;
fn check_number(&self, number: i32) -> Option<NegativeError> {
if number < 0 {
Some(NegativeError { number: number })
} else {
None
}
}
}
impl Error for NegativeError {
fn description(&self) -> String {
format!("{} is negative", self.number)
}
}
I know that in this example, the two structs look identical, but in my code, there are many different structs, so I can't merge them. Lastly, an example main function, to illustrate the kind of thing I want to do:
fn main() {
let numbers = vec![1, -4, 64, -25];
let checks = vec![
Box::new(EvenCheck) as Box<Check<Error = Error>>,
Box::new(NegativeCheck) as Box<Check<Error = Error>>,
]; // What should I put for this Vec's type?
for number in numbers {
for check in checks {
if let Some(error) = check.check_number(number) {
println!("{:?} - {}", error, error.description())
}
}
}
}
You can see the code in the Rust playground.
Solutions I've Tried
The closest thing I've come to a solution is to remove the associated types and have the checks return Option<Box<Error>>. However, I get this error instead:
error[E0038]: the trait `Error` cannot be made into an object
--> src/main.rs:4:55
|
4 | fn check_number(&self, number: i32) -> Option<Box<Error>>;
| ^^^^^ the trait `Error` cannot be made into an object
|
= note: the trait cannot use `Self` as a type parameter in the supertraits or where-clauses
because of the PartialEq in the Error trait. Rust has been great to me thus far, and I really hope I'm able to bend the type system into supporting something like this!
When you write an impl Check and specialize your type Error with a concrete type, you are ending up with different types.
In other words, Check<Error = NegativeError> and Check<Error = EvenError> are statically different types. Although you might expect Check<Error> to describe both, note that in Rust NegativeError and EvenError are not sub-types of Error. They are guaranteed to implement all methods defined by the Error trait, but then calls to those methods will be statically dispatched to physically different functions that the compiler creates (each will have a version for NegativeError, one for EvenError).
Therefore, you can't put them in the same Vec, even boxed (as you discovered). It's not so much a matter of knowing how much space to allocate, it's that Vec requires its types to be homogeneous (you can't have a vec![1u8, 'a'] either, although a char is representable as a u8 in memory).
Rust's way to "erase" some of the type information and gain the dynamic dispatch part of subtyping is, as you discovered, trait objects.
If you want to give another try to the trait object approach, you might find it more appealing with a few tweaks...
You might find it much easier if you used the Error trait in std::error instead of your own version of it.
You may need to impl Display to create a description with a dynamically built String, like so:
impl fmt::Display for EvenError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} is even", self.number)
}
}
impl Error for EvenError {
fn description(&self) -> &str { "even error" }
}
Now you can drop the associated type and have Check return a trait object:
trait Check {
fn check_number(&self, number: i32) -> Option<Box<Error>>;
}
your Vec now has an expressible type:
let mut checks: Vec<Box<Check>> = vec![
Box::new(EvenCheck) ,
Box::new(NegativeCheck) ,
];
The best part of using std::error::Error...
is that now you don't need to use PartialEq to understand what error was thrown. Error has various types of downcasts and type checks if you do need to retrieve the concrete Error type out of your trait object.
for number in numbers {
for check in &mut checks {
if let Some(error) = check.check_number(number) {
println!("{}", error);
if let Some(s_err)= error.downcast_ref::<EvenError>() {
println!("custom logic for EvenErr: {} - {}", s_err.number, s_err)
}
}
}
}
full example on the playground
I eventually found a way to do it that I'm happy with. Instead of having a vector of Box<Check<???>> objects, have a vector of closures that all have the same type, abstracting away the very functions that get called:
fn main() {
type Probe = Box<Fn(i32) -> Option<Box<Error>>>;
let numbers: Vec<i32> = vec![ 1, -4, 64, -25 ];
let checks = vec![
Box::new(|num| EvenCheck.check_number(num).map(|u| Box::new(u) as Box<Error>)) as Probe,
Box::new(|num| NegativeCheck.check_number(num).map(|u| Box::new(u) as Box<Error>)) as Probe,
];
for number in numbers {
for check in checks.iter() {
if let Some(error) = check(number) {
println!("{}", error.description());
}
}
}
}
Not only does this allow for a vector of Box<Error> objects to be returned, it allows the Check objects to provide their own Error associated type which doesn't need to implement PartialEq. The multiple ases look a little messy, but on the whole it's not that bad.
I'd suggest you some refactoring.
First, I'm pretty sure, that vectors should be homogeneous in Rust, so there is no way to supply elements of different types for them. Also you cannot downcast traits to reduce them to a common base trait (as I remember, there was a question about it on SO).
So I'd use algebraic type with explicit match for this task, like this:
enum Checker {
Even(EvenCheck),
Negative(NegativeCheck),
}
let checks = vec![
Checker::Even(EvenCheck),
Checker::Negative(NegativeCheck),
];
As for error handling, consider use FromError framework, so you will able to involve try! macro in your code and to convert error types from one to another.