Using traits as types in enums - enums

Here's my code:
trait UnaryOperator {
fn apply(&self, expr: Expression) -> Expression;
}
pub enum Expression {
UnaryOp(UnaryOperator, Expression),
Value(i64)
}
Which gives the following errors:
error: the trait 'core::marker::sized' is not implemented for type 'parser::UnaryOperator'
note: 'parser::UnaryOperator' does not have a constant size known at compile-time
I'm not sure how to accomplish what I want. I've tried:
trait UnaryOperator: Sized {
...
}
As well as
pub enum Expression {
UnaryOp(UnaryOperator + Sized, Expression),
...
}
And neither solved the issue.
I've seen ways to possibly accomplish what I want with generics, but then it seems like two expressions with different operators would be different types, but that's not what I want. I want all expressions to be the same type regardless of what the operators are.

Traits do not have a known size - they are unsized. To see why, check this out:
trait AddOne {
fn add_one(&self) -> u8;
}
struct Alpha {
a: u8,
}
struct Beta {
a: [u8; 1024],
}
impl AddOne for Alpha {
fn add_one(&self) -> { 0 }
}
impl AddOne for Beta {
fn add_one(&self) -> { 0 }
}
Both Alpha and Beta implement AddOne, so how big should some arbitrary AddOne be? Oh, and remember that other crates may implement your trait sometime in the future.
That's why you get the first error. There are 3 main solutions (note that none of these solutions immediately fix your problem...):
Use a Box<Trait>. This is kind-of-similar-but-different to languages like Java, where you just accept an interface. This has a known size (a pointer's worth) and owns the trait. This has the downside of requiring an allocation.
trait UnaryOperator {
fn apply(&self, expr: Expression) -> Expression;
}
pub enum Expression {
UnaryOp(Box<UnaryOperator>, Expression),
Value(i64)
}
Use a reference to the trait. This also has a known size (a pointer's worth two pointers' worth, see Matthieu M.'s comment). The downside is that something has to own the object and you need to track the lifetime:
trait UnaryOperator {
fn apply<'a>(&self, expr: Expression<'a>) -> Expression<'a>;
}
pub enum Expression<'a> {
UnaryOp(&'a UnaryOperator, Expression<'a>),
Value(i64)
}
Use a generic. This has a fixed size because each usage of the enum will have been specialized for the specific type. This has the downside of causing code bloat if you have many distinct specializations. Update As you point out, this means that Expression<A> and Expression<B> would have different types. Depending on your usage, this could be a problem. You wouldn't be able to easily create a Vec<Expression<A>> if you had both.
trait UnaryOperator {
fn apply<U>(&self, expr: Expression<U>) -> Expression<U>;
}
pub enum Expression<U>
where U: UnaryOperator
{
UnaryOp(U, Expression<U>),
Value(i64)
}
Now, all of these fail as written because you have a recursive type definition. Let's look at this simplification:
enum Expression {
A(Expression),
B(u8),
}
How big is Expression? Well, it needs to have enough space to hold... an Expression! Which needs to be able to hold an Expression.... you see where this is going.
You need to add some amount of indirection here. Similar concepts to #1 and #2 apply - you can use a Box or a reference to get a fixed size:
enum Expression {
A(Box<Expression>),
B(u8),
}

Related

In rust, what's the idiomatic way of expressing a struct that can be ordered, but only in reference to a standard value?

I'm trying to implement a game similar to GeoGuessr, where players enter geographic coordinates according to a street-view image, and are ranked by their distance to the correct location of the image.
I need a data structure to represent the submission of a player, and I want it to implement PartialEq and PartialOrd so that it can be easily sorted within container structures. However, unlike ordinary PartialOrd structures that are comparable by themselves, my structure is only comparable in reference to the correct answer.
I would like the rankings to be accessible at any time, so I'd prefer a container that always maintains the order of its elements to avoid the sorting costs, in my case I chose skiplist::ordered_skiplist::OrderedSkipList. That means methods like sort_by_key are unavailable to me, and I have to implement PartialOrd for my structure.
So I ended up keeping a reference to the correct answer as a field in my structure:
struct Submission<'a> {
submitted: Location,
correct: &'a Location,
}
impl Submission<'_> {
fn distance(&self) -> f64 {
self.submitted.distance(*self.correct)
}
}
impl PartialEq for Submission<'_> {
fn eq(&self, other: &Self) -> bool {
let d1 = self.distance();
let d2 = other.distance();
d1.eq(&d2)
}
}
impl PartialOrd for Submission<'_> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
let d1 = self.distance();
let d2 = other.distance();
d1.partial_cmp(&d2)
}
}
But this doesn't seem idiomatic to me, as it doesn't restrict comparison between Submissions with different correct references, which would be invalid. Also, maintaining the same correct reference in each Submission seems a redundant cost. Is there a more idiomatic way of defining the data structure for this scenario?
Edit:
I've considered comparing the correct references in partial_cmp and returning None for invalid comparisons, but that's also redundant for my case as I can prevent this in coding myself. I'm looking for a compile-time way of preventing invalid comparisons, rather than a runtime one.

How do I constrain associated types on a non-owned trait? [duplicate]

I have this code:
extern crate serde;
use serde::de::DeserializeOwned;
use serde::Serialize;
trait Bar<'a, T: 'a>
where
T: Serialize,
&'a T: DeserializeOwned,
{
}
I would like to write this using an associated type, because the type T is unimportant to the users of this type. I got this far:
trait Bar {
type T: Serialize;
}
I cannot figure out how to specify the other bound.
Ultimately, I want to use a function like this:
extern crate serde_json;
fn test<I: Bar>(t: I::T) -> String {
serde_json::to_string(&t).unwrap()
}
The "correct" solution is to place the bounds on the trait, but referencing the associated type. In this case, you can also use higher ranked trait bounds to handle the reference:
trait Bar
where
Self::T: Serialize,
// ^^^^^^^ Bounds on an associated type
for<'a> &'a Self::T: DeserializeOwned,
// ^^^^^^^^^^^ Higher-ranked trait bounds
{
type T;
}
However, this doesn't work yet.
I believe that you will need to either:
wait for issue 20671 and/or issue 50346 to be fixed.
wait for the generic associated types feature which introduces where clauses on associated types.
In the meantime, the workaround is to duplicate the bound everywhere it's needed:
fn test<I: Bar>(t: I::T) -> String
where
for<'a> &'a I::T: DeserializeOwned,
{
serde_json::to_string(&t).unwrap()
}

What does the 'where' clause within a trait do?

If I have this code:
trait Trait {
fn f(&self) -> i32 where Self: Sized;
fn g(&self) -> i32;
}
fn object_safety_dynamic(x: &Trait) {
x.f(); // error
x.g(); // works
}
What does the where clause actually do?
Naively, I was thinking where Self: Sized; dictates something about the type implementing Trait, like 'if you implement Trait for type A your type A must be sized, i.e., it can be i32 but not [i32].
However, such a constraint would rather go as trait Trait: Sized (correct me if I am wrong)?
Now I noticed where Self: Sized; actually determines if I can call f or g from within object_safety_dynamic.
My questions:
What happens here behind the scenes?
What (in simple English) am I actually telling the compiler by where Self: Sized; that makes g() work but f() not?
In particular: Since &self is a reference anyway, what compiled difference exists between f and g for various (sized or unsized) types. Wouldn't it always boil down to something like _vtable_f_or_g(*self) -> i32, regardless of where or if the type is sized or not?
Why can I implement Trait for both u8 and [u8]. Shouldn't the compiler actually stop me from implementing f() for [u8], instead of throwing an error at the call site?
fn f(&self) -> i32 where Self: Sized;
This says that f is only defined for types that also implement Sized. Unsized types may still implement Trait, but f will not be available.
Inside object_safety_dynamic, calling x.f() is actually doing: (*x).f(). While x is sized because it's a pointer, *x might not be because it could be any implementation of Trait. But code inside the function has to work for any valid argument, so you are not allowed to call x.f() there.
What does the where clause actually do?
Naively, I was thinking where Self: Sized; dictates something about the type implementing Trait, like 'if you implement Trait for type A your type A must be sized, i.e., it can be i32 but not [i32].
However, such a constraint would rather go as trait Trait: Sized
This is correct.
However, in this case, the bound applies only to the function. where bounds on functions are only checked at the callsite.
What happens here behind the scenes?
There is a confusing bit about rust's syntax which is that Trait can refer to either
The trait Trait; or
The "trait object" Trait, which is actually a type, not an object.
Sized is a trait, and any type T that is Sized may have its size taken as a constant, by std::mem::size_of::<T>(). Such types that are not sized are str and [u8], whose contents do not have a fixed size.
The type Trait is also unsized. Intuitively, this is because Trait as a type consists of all values of types that implement the trait Trait, which may have varying size. This means you can never have a value of type Trait - you can only refer to one via a "fat pointer" such as &Trait or Box<Trait> and so on. These have the size of 2 pointers - one for a vtable, one for the data. It looks roughly like this:
struct &Trait {
pub data: *mut (),
pub vtable: *mut (),
}
There is automatically an impl of the form:
impl Trait /* the trait */ for Trait /* the type */ {
fn f(&self) -> i32 where Self: Sized { .. }
fn g(&self) -> i32 {
/* vtable magic: something like (self.vtable.g)(self.data) */
}
}
What (in simple English) am I actually telling the compiler by where Self: Sized; that makes g() work but f() not?
Note that since, as I mentioned, Trait is not Sized, the bound Self: Sized is not satisfied and so the function f cannot be called where Self == Trait.
In particular: Since &self is a reference anyway, what compiled difference exists between f and g for various (sized or unsized) types. Wouldn't it always boil down to something like _vtable_f_or_g(*self) -> i32, regardless of where or if the type is sized or not?
The type Trait is always unsized. It doesn't matter which type has been coerced to Trait. The way you call the function with a Sized variable is to use it directly:
fn generic<T: Trait + Sized>(x: &T) { // the `Sized` bound is implicit, added here for clarity
x.f(); // compiles just fine
x.g();
}
Why can I implement Trait for both u8 and [u8]. Shouldn't the compiler actually stop me from implementing f() for [u8], instead of throwing an error at the call site?
Because the trait is not bounded by Self: Sized - the function f is. So there is nothing stopping you from implementing the function - it's just that the bounds on the function can never be satisfied, so you can never call it.

Can Rust attributes be used for something like mapping URL routes to functions? [duplicate]

I've gotten as far as having the custom attribute invoked:
#[plugin_registrar]
pub fn registrar(reg: &mut rustc::plugin::Registry) {
use syntax::parse::token::intern;
use syntax::ext::base;
// Register the `#[dummy]` attribute.
reg.register_syntax_extension(intern("dummy"),
base::ItemDecorator(dummy_expand));
}
// Decorator for `dummy` attribute
pub fn dummy_expand(context: &mut ext::base::ExtCtxt, span: codemap::Span, meta_item: Gc<ast::MetaItem>, item: Gc<ast::Item>, push: |Gc<ast::Item>|) {
match item.node {
ast::ItemFn(decl, ref style, ref abi, ref generics, block) => {
trace!("{}", decl);
// ...? Add something here.
}
_ => {
context.span_err(span, "dummy is only permissiable on functions");
}
}
}
Invoked via:
#![feature(phase)]
#[phase(plugin)]
extern crate dummy_ext;
#[test]
#[dummy]
fn hello() {
println!("Put something above this...");
}
...and I've seen a few examples around of things that use quote_expr!( ... ) to do this, but I don't really understand them.
Let's say I want to add this statement (or is it expression?) to the top of any function tagged #[dummy]:
println!("dummy");
How do I achieve that?
There's two tasks here:
creating the AST you wish to insert
transforming the AST of some function (e.g. inserting another piece)
Notes:
when I say "item" in this answer, I specifically meant the item AST node, e.g. fn, struct, impl.
when doing anything with macros, rustc --pretty expanded foo.rs is your best friend (works best on smallest examples, e.g. avoiding #[deriving] and println!, unless you're trying to debug those specifically).
AST creation
There's 3 basic ways to create chunks of AST from scratch:
manually writing out the structs & enums,
using the methods of AstBuilder to abbreviate that, and
using quotation to avoid that altogether.
In this case, we can use quoting, so I won't waste time on the others. The quote macros take an ExtCtxt ("extension context") and an expression or item etc. and create an AST value that represents that item, e.g.
let x: Gc<ast::Expr> = quote_expr!(cx, 1 + 2);
creates an Expr_ with value ExprBinary, that contains two ExprLits (for the 1 and 2 literals).
Hence, to create the desired expression, quote_expr!(cx, println!("dummy")) should work. Quotation is more powerful than just this: you can use $ it to splice a variable storing AST into an expression, e.g., if we have the x as above, then
let y = quote_expr!(cx, if $x > 0 { println!("dummy") });
will create an AST reprsenting if 1 + 2 > 0 { println!("dummy") }.
This is all very unstable, and the macros are feature gated. A full "working" example:
#![feature(quote)]
#![crate_type = "dylib"]
extern crate syntax;
use syntax::ext::base::ExtCtxt;
use syntax::ast;
use std::gc::Gc;
fn basic_print(cx: &mut ExtCtxt) -> Gc<ast::Expr> {
quote_expr!(cx, println!("dummy"))
}
fn quoted_print(cx: &mut ExtCtxt) -> Gc<ast::Expr> {
let p = basic_print(cx);
quote_expr!(cx, if true { $p })
}
As of 2014-08-29, the list of quoting macros is: quote_tokens, quote_expr, quote_ty, quote_method, quote_item, quote_pat, quote_arm, quote_stmt. (Each essentially creates the similarly-named type in syntax::ast.)
(Be warned: they are implemented in a very hacky way at the moment, by just stringifying their argument and reparsing, so it's relatively easy to encounter confusing behaviour.)
AST transformation
We now know how to make isolated chunks of AST, but how can we feed them back into the main code?
Well, the exact method depends on what you are trying to do. There's a variety of different types of syntax extensions.
If you just wanted to expand to some expression in place (like println!), NormalTT is correct,
if you want to create new items based on an existing one, without modifying anything, use ItemDecorator (e.g. #[deriving] creates some impl blocks based on the struct and enum items to which it is attached)
if you want to take an item and actually change it, use ItemModifier
Thus, in this case, we want an ItemModifier, so that we can change #[dummy] fn foo() { ... } into #[dummy] fn foo() { println!("dummy"); .... }. Let's declare a function with the right signature:
fn dummy_expand(cx: &mut ExtCtxt, sp: Span, _: Gc<ast::MetaItem>, item: Gc<ast::Item>) -> Gc<Item>
This is registered with
reg.register_syntax_extension(intern("dummy"), base::ItemModifier(dummy_expand));
We've got the boilerplate set-up, we just need to write the implementation. There's two approaches. We could just add the println! to the start of the function's contents, or we could change the contents from foo(); bar(); ... to println!("dummy"); { foo(); bar(); ... } by just creating two new expressions.
As you found, an ItemFn can be matched with
ast::ItemFn(decl, ref style, ref abi, ref generics, block)
where block is the actual contents. The second approach I mention above is easiest, just
let new_contents = quote_expr!(cx,
println!("dummy");
$block
);
and then to preserve the old information, we'll construct a new ItemFn and wrap it back up with the right method on AstBuilder. In total:
#![feature(quote, plugin_registrar)]
#![crate_type = "dylib"]
// general boilerplate
extern crate syntax;
extern crate rustc;
use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, ItemModifier};
// NB. this is important or the method calls don't work
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
use std::gc::Gc;
#[plugin_registrar]
pub fn registrar(reg: &mut rustc::plugin::Registry) {
// Register the `#[dummy]` attribute.
reg.register_syntax_extension(token::intern("dummy"),
ItemModifier(dummy_expand));
}
fn dummy_expand(cx: &mut ExtCtxt, sp: Span, _: Gc<ast::MetaItem>,
item: Gc<ast::Item>) -> Gc<ast::Item> {
match item.node {
ast::ItemFn(decl, ref style, ref abi, ref generics, block) => {
let new_contents = quote_expr!(&mut *cx,
println!("dummy");
$block
);
let new_item_ = ast::ItemFn(decl, style.clone(),
abi.clone(), generics.clone(),
// AstBuilder to create block from expr
cx.block_expr(new_contents));
// copying info from old to new
cx.item(item.span, item.ident, item.attrs.clone(), new_item_)
}
_ => {
cx.span_err(sp, "dummy is only permissible on functions");
item
}
}
}

Pattern matching over borrowed HashMap containing enums

I'm trying to learn Rust, so bear with me if I'm way off :-)
I have a program that inserts enums into a HashMap, and uses Strings as keys. I'm trying to match over the content of the HashMap. Problem is that I can't figure out how to get the correct borrowings, references and types in the eval_output function. How should the eval_output function look to properly handle a reference to a HashMap? Is there any good document that I can read to learn more about this particular subject?
use std::prelude::*;
use std::collections::HashMap;
enum Op {
Not(String),
Value(u16),
}
fn eval_output(output: &str, outputs: &HashMap<String, Op>) -> u16 {
match outputs.get(output) {
Some(&op) => {
match op {
Op::Not(input) => return eval_output(input.as_str(), outputs),
Op::Value(value) => return value,
}
}
None => panic!("Did not find input for wire {}", output),
}
}
fn main() {
let mut outputs = HashMap::new();
outputs.insert(String::from("x"), Op::Value(17));
outputs.insert(String::from("a"), Op::Not(String::from("x")));
println!("Calculated output is {}", eval_output("a", &outputs));
}
Review what the compiler error message is:
error: cannot move out of borrowed content [E0507]
Some(&op) => {
^~~
note: attempting to move value to here
Some(&op) => {
^~
help: to prevent the move, use `ref op` or `ref mut op` to capture value by reference
While technically correct, using Some(ref op) would be a bit silly, as the type of op would then be a double-reference (&&Op). Instead, we simply remove the & and have Some(op).
This is a common mistake that bites people, because to get it right you have to be familiar with both pattern matching and references, plus Rust's strict borrow checker. When you have Some(&op), that says
Match an Option that is the variant Some. The Some must contain a reference to a value. The referred-to thing should be moved out of where it is and placed into op.
When pattern matching, the two keywords ref and mut can come into play. These are not pattern-matched, but instead they control how the value is bound to the variable name. They are analogs of & and mut.
This leads us to the next error:
error: mismatched types:
expected `&Op`,
found `Op`
Op::Not(input) => return eval_output(input.as_str(), outputs),
^~~~~~~~~~~~~~
It's preferred to do match *some_reference, when possible, but in this case you cannot. So we need to update the pattern to match a reference to an Op — &Op. Look at what error comes next...
error: cannot move out of borrowed content [E0507]
&Op::Not(input) => return eval_output(input.as_str(), outputs),
^~~~~~~~~~~~~~~
It's our friend from earlier. This time, we will follow the compilers advice, and change it to ref input. A bit more changes and we have it:
use std::collections::HashMap;
enum Op {
Not(String),
Value(u16),
}
fn eval_output(output: &str, outputs: &HashMap<String, Op>) -> u16 {
match outputs.get(output) {
Some(op) => {
match op {
&Op::Not(ref input) => eval_output(input, outputs),
&Op::Value(value) => value,
}
}
None => panic!("Did not find input for wire {}", output),
}
}
fn main() {
let mut outputs = HashMap::new();
outputs.insert("x".into(), Op::Value(17));
outputs.insert("a".into(), Op::Not("x".into()));
println!("Calculated output is {}", eval_output("a", &outputs));
}
There's no need to use std::prelude::*; — the compiler inserts that automatically.
as_str doesn't exist in stable Rust. A reference to a String (&String) can use deref coercions to act like a string slice (&str).
I used into instead of String::from as it's a bit shorter. No real better reason.

Resources