I am trying to implement a function-like macro in Rust. It should be used as follows:
service!(FooService, "/foo_service");
This macro call shall generate a struct FooServiceClient and an impl FooServiceClient with various methods.
I am using the syn + quote duo and here are some relevant bits of my code:
struct ServiceDefinition {
pub service_name: String,
pub scope: String,
pub operations: Vec<Operation>,
}
impl Parse for ServiceDefinition {
fn parse(input: ParseStream) -> Result<Self> {
let params = Punctuated::<Expr, Token![,]>::parse_terminated(&input)?;
let service_name = {
let expr = ¶ms[0];
match expr {
Expr::Path(ref expr_path) => {
let leading_colon = expr_path.path.leading_colon;
if let Some(leading_colon) = leading_colon {
return Err(Error::new(expr_path.span(), "expected unscoped identifier"));
}
if expr_path.path.segments.len() != 1 {
return Err(Error::new(expr_path.span(), "expected unscoped identifier"));
}
expr_path.path.segments.first().unwrap().ident
},
_ => {
return Err(Error::new(expr.span(), "expected service name"));
}
}
};
Ok(ServiceDefinition {
service_name: service_name.to_string(),
scope: "/foo".to_string(),
operations: vec![],
})
}
}
As you can see, this is still at the early stages of work in progress, but when I try to compile it, this is the error I'm getting:
error[E0599]: no method named `span` found for reference `&ExprPath` in the current scope
--> service_commons/src/lib.rs:58:57
|
58 | return Err(Error::new(expr_path.span(), "expected unscoped identifier"));
| ^^^^ method not found in `&ExprPath`
|
As far as I can see in the docs, the method span should be on these types, but it's obviously missing for some reason.
I am using syn = {version = "1.0.70", features = ["full"]} in my Cargo file.
What am I missing here?
span comes from the Spanned trait, so it needs to be in scope:
use syn::spanned::Spanned;
See also:
Why do I need to import a trait to use the methods it defines for a type?
Related
I get an error referencing a boxed value:
error[E0308]: mismatched types
--> src/interpreter.rs:284:37
|
284 | table[index].function = method;
| ^^^^^^ expected struct `Box`, found `&Box<MethodType>`
|
= note: expected struct `Box<MethodType>`
found reference `&Box<MethodType>`
If I dereference it by saying *method I get an error because MethodType doesn't implement Copy (which would be a pain, because it includes a couple of Vecs and it would be very expensive to copy). Maybe I could fix it with ManuallyDrop, but that doesn't seem very idiomatic, and I spent some time this afternoon getting rid of a ManuallyDrop.
Perhaps I shouldn't be passing it by reference (the only two calls to addMethodToTable are in the following method, addMethodToDispatch but they are in loops, because I need to try allocation several times). Maybe it should be an Rc instead.
This is part of a lot of code, so I hope this snippit is enough to show what I'm doing wrong:
#[derive(Clone)]
enum MethodType {
Function(Function),
Method(Method),
NoType,
}
#[derive(Clone)]
struct MethodMatch {
selector: usize,
function: Box<MethodType>,
}
pub struct Dispatch {
class: Object,
table: Box<[MethodMatch]>,
}
fn addMethodToTable(table:&mut Vec<MethodMatch>,selector:usize,method:&Box<MethodType>) -> bool {
let len = table.capacity();
let hash = selector%(len-DISPATCH_OVERFLOW);
for index in hash..hash+DISPATCH_OVERFLOW {
if let SelectorMatch::Other = table[index].selector_match(selector) {
// slot taken
} else {
table[index].selector = selector;
table[index].function = method;
return true
}
}
false
}
fn addMethodToDispatch(class:ClassIndex,selector:Object,method:Box<MethodType>) {
let selector = selector.raw()>>3;
while ... {
table = Vec::with_capacity(count+DISPATCH_OVERFLOW);
addMethodToTable(&mut table,selector,&method);
for mm in disp.table.iter() {
if addMethodToTable(&mut table,mm.selector,&mm.function) {unfinished = true;break}
}
}
table.resize(table.capacity(),Default::default());
disp.table=table.into_boxed_slice()
},
index => {
disp.table[index].selector = selector;
disp.table[index].function = method
},
I was having such a productive day until I hit this... thinking I was finally "getting" Rust!
Your struct expects an owned type - function must be a Box:
#[derive(Clone)]
struct MethodMatch {
selector: usize,
function: Box<MethodType>,
}
but in your other method the parameter method is a reference to a Box which are two very different types:
`fn addMethodToTable(....,method: &Box<MethodType>)
There are two ways to fix the issue (not all may be applicable in your case):
Change the reference to an owned type: method: &Box<MethodType> should become method: Box<MethodType>
Because your MethodType implements Clone, just clone it in order to get an owned type from the reference: table[index].function = method.clone();
If you are free to change the struct definition, you can use Rc<T> instead of Box<T>. Thus you can use Rc::clone(reference) and clone only the pointer instead of the whole struct.
I am quite new to Rust and I'm strugging in solving a generic type problem in a particular pattern.
Let's say I have a structure as following
#[derive(Debug)]
struct Test<T>
where T: Eq
{
pub value: Option<T>,
pub array: Option<Vec<Test<T>>>
}
and I failed to use it when array contains different actual type.
fn main() {
let a = Test {
value:Some(16),
array:None
};
let b = Test {
value:Some("abc"),
array:None
};
let c = Test {
value:None,
array:Some(vec![a,b])
};
println!("{:?}",c);
}
compiled error:
|
24 | array:Some(vec![a,b])
| ^ expected integer, found `&str`
|
= note: expected struct `Test<{integer}>`
found struct `Test<&str>`
how to change the definition of Test to achieve my goal?
Thanks.
Some edit after adopting the answer:
while I changed to Box:
#[derive(Debug)]
struct Test
{
pub value:Option<Box<dyn Eq>>,
pub array: Option<Vec<Test>>
}
fn main() {
let a = Test {
value:Some(Box::new(16)),
array:None
};
let b = Test {
value:Some(Box::new("123")),
array:None
};
let c = Test {
value:None,
array:Some(vec![a,b])
};
println!("{:?}",c);
}
5 | pub value:Option<Box<dyn Eq>>,
| ^^^^^^ `Eq` cannot be made into an object
Some edit 2
by using Any trait, here compiles:
use std::any::Any;
#[derive(Debug)]
struct Test
{
pub value:Option<Box<dyn Any>>,
pub array: Option<Vec<Test>>
}
fn main() {
let a = Test {
value:Some(Box::new(16)),
array:None
};
let b = Test {
value:Some(Box::new("123")),
array:None
};
let c = Test {
value:None,
array:Some(vec![a,b])
};
println!("{:?}",c);
}
output:
Test { value: None, array: Some([Test { value: Some(Any), array: None }, Test { value: Some(Any), array: None }]) }
This is an error in expection. While Test is generic over T, that doesn't mean that all Test can be used interchangeably, without respect to what T actually is. In your case, a is a Test<{integer}>, while b is a Test<&'static str>. The compiler has inferred what the actual types are, filling in the blank that is the generic type parameter T.
In c, the compiler will figure out that Vec<T> is initialized by a Test<{integer} (because a is a Test<{integer}), and since all T for that Vec have to be the same, the second parameter (b) also has to be a Test<{integer}>.
You can either use an enum in the definition of Test to provide for both cases (&str and u32 for example). Or you use dynamic dispatch, e.g. via a Box<dyn Eq>. However, since you are apparently going to compare values of different types, the enum-approach is probably the only viable, since you can't easily compare completely unknown types for equality.
I am experimenting with Rust procedural macros.
I would like to be able to create a macro that would be used to generate JNI invocation boilerplate. Something like
jni_method!{com.purplefrog.rust_callable.Widget, fn echo_str(&str)->String}
I have the following code so far (playground):
#[macro_use]
extern crate syn; // 1.0.33
use syn::parse::{Parse, ParseStream};
use syn::Signature;
struct Arguments {
name: proc_macro2::Ident,
signature: Signature,
}
impl Parse for Arguments {
fn parse(tokens: ParseStream) -> Result<Arguments, syn::Error> {
let name: proc_macro2::Ident = tokens.parse()?;
let comma: Token![,] = tokens.parse()?;
let signature: Signature = //tokens.parse()?;
syn::item::parsing::parse_signature(tokens)?;
Ok(Arguments {
name: name,
signature,
})
}
}
Unfortunately, the parse_signature call is in error:
error[E0603]: module `item` is private
--> src/lib.rs:17:18
|
17 | syn::item::parsing::parse_signature(tokens)?;
| ^^^^ private module
|
note: the module `item` is defined here
--> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/syn-1.0.33/src/lib.rs:363:1
|
363 | mod item;
| ^^^^^^^^^
What is the proper way to parse a Signature from a ParseStream?
Why do you need a Signature? Depending on what you are actually trying to parse, you should use one of the following:
Fn* trait signature (ex. FnMut(usize) -> bool)
Parse into syn::TraitBound (in order to catch lifetime bounds not present in a just a path), then you can get the inputs/output from the parenthesized arguments of the last segment of the trait bound's path.
Bare function, aka function pointer (ex. fn(usize) -> bool)
Parse into syn::TypeBareFn, then you can get the inputs/output directly.
Function definition, including a body (ex. fn foo(x: usize) -> bool { x > 5 })
Parse into syn::ItemFn, which includes a signature.
Foreign function definition (ex. fn foo(x: usize) -> bool)
Parse into Struct syn::ForeignItemFn, which includes a signature. Note that this is meant for declarations in extern blocks, so chances are this is not actually what you are looking for.
I eventually found an example of how to work around this problem ( https://github.com/dtolnay/syn/blob/master/examples/lazy-static/lazy-static/src/lib.rs ). You are supposed to create your own struct and impl Parse for it. I was able to build my own syntax from Parseable elements.
struct MySignature {
pub parameter_types: Vec<Type>,
}
impl Parse for MySignature {
fn parse(tokens: ParseStream) -> Result<Self, syn::Error> {
let mut parameter_types: Vec<Type> = Vec::new();
let arg_types: ParseBuffer;
parenthesized!(arg_types in tokens);
while !arg_types.is_empty() {
let arg_type: Type = arg_types.parse()?;
parameter_types.push(arg_type);
if !arg_types.is_empty() {
let _comma: Token![,] = arg_types.parse()?;
}
}
Ok(MySignature { parameter_types })
}
}
I have the following enum defined:
#[derive(Debug, Copy, Clone)]
struct Core;
#[derive(Debug, Copy, Clone)]
struct Mem;
#[derive(Debug, Copy, Clone)]
pub enum Atag {
Core(Core),
Mem(Mem),
Cmd(&'static str),
Unknown(u32),
None,
}
I would like to implement a function on this enum which "filters out" certain enum values. I have the following:
impl Atag {
/// Returns `Some` if this is a `Core` ATAG. Otherwise returns `None`.
pub fn core(self) -> Option<Core> {
match self {
Atag::Core => Some(self),
_ => None
}
}
}
I'm not sure why, but the compiler complains:
error[E0532]: expected unit struct/variant or constant, found tuple variant `Atag::Core`
--> src/main.rs:17:13
|
17 | Atag::Core => Some(self),
| ^^^^^^^^^^ not a unit struct/variant or constant
help: possible better candidate is found in another module, you can import it into scope
|
1 | use Core;
|
I also tried a comparison approach:
pub fn core(self) -> Option<Core> {
if self == Atag::Core {
Some(self)
} else {
None
}
}
But the compiler complains:
error[E0369]: binary operation `==` cannot be applied to type `Atag`
--> src/main.rs:20:12
|
20 | if self == Atag::Core {
| ^^^^^^^^^^^^^^^^^^
|
= note: an implementation of `std::cmp::PartialEq` might be missing for `Atag`
I think this is just a limitation of the pattern matching and is designed to prevent unexpected behavior.
The full "definition" of an Atag with type Core is Atag::Core(raw::Core). Obviously, the contents of the Core are irrelevant to you, but the compiler needs to know that everything is "accounted for" because the compiler is a stickler for the rules. The easiest way to get around this is to use the "anything pattern", _, much like you did to match non-Core variants.
impl Atag {
/// Returns `Some` if this is a `Core` ATAG. Otherwise returns `None`.
pub fn core(self) -> Option<Core> {
match self {
// The compiler now knows that a value is expected,
// but isn't necessary for the purposes of our program.
Atag::Core(_) => Some(self),
_ => None
}
}
}
To ignore multiple values, you'd use Something::Foo(_, _) - one underscore for each value in the variant, or Something::Foo(..) to ignore everything.
Remember that, unlike in some other languages, a Rust enum is not "just" a collection of different types. Data associated with an enum value is a part of it, just like the fields of a structure. So self == Atag::Core isn't a meaningful statement because it ignores the data associated with a Core. A Foo(0) is different than a Foo(12), even if they're both of the Foo variant.
I'd also like to point out if let, which is - as far as I can tell - the closest option to a standard if statement without defining a custom is_core function on Atag (which, given the existence of match and if let, is basically unnecessary).
impl Atag {
/// Returns `Some` if this is a `Core` ATAG. Otherwise returns `None`.
pub fn core(self) -> Option<Core> {
if let Atag::Core(_) = self {
Some(self)
} else {
None
}
}
}
I needed something like this to chain functions together nicely. In that case, you want to return the unwrapped core type, rather than just the enum.
I also found it easier to not consume the input, and so accepted a &self argument an returned an Option<&Core>. But you can have both.
The Rust convention has as_X as the reference-based conversion and into_X as the conversion that consumes the value. For example:
impl Atag {
fn as_core(&self) -> Option<&Core> {
if let Atag::Core(ref v) = self {
Some(v)
} else {
None
}
}
fn into_core(self) -> Option<Core> {
if let Atag::Core(v) = self {
Some(v)
} else {
None
}
}
}
fn main() {
let c = Atag::Core(Core {});
let m = Atag::Mem(Mem {});
assert_eq!(c.as_core().map(|cc| "CORE_REF"), Some("CORE_REF"));
assert_eq!(m.as_core().map(|cc| "CORE_REF"), None);
// Consume c - we cant use it after here...
assert_eq!(c.into_core().map(|cc| "NOM NOM CORE"), Some("NOM NOM CORE"));
// Consume m - we cant use it after here...
assert_eq!(m.into_core().map(|cc| "NOM NOM CORE"), None);
}
Faced a weird case of repeated argument mismatch while creating a macro in Rust:
use std::mem;
trait Object {}
#[derive(Debug)]
struct This {}
impl Object for This {}
#[derive(Debug)]
struct That {}
impl Object for That {}
macro_rules! types {
($($fname:ident),*) => {
enum Type {
$($fname),*
}
fn match_it(t: Type, b: Box<Object>) {
let p = match t {
$(
Type::$fname => {
mem::transmute::<Box<Object>, Box<$fname>>(b)
}
),*
};
}
}
}
types!(This, That);
fn main() {}
It results in:
error: match arms have incompatible types [--explain E0308]
--> <anon>:20:21
20 |> let p = match t {
|> ^ expected struct `This`, found struct `That`
<anon>:31:1: 31:20: note: in this expansion of types! (defined in <anon>)
note: expected type `Box<This>`
note: found type `Box<That>`
note: match arm with an incompatible type
--> <anon>:22:33
22 |> Type::$fname => {
|> ^
<anon>:31:1: 31:20: note: in this expansion of types! (defined in <anon>)
Shouldn't the $fname of enum be the same as $fname of Box if they share the same loop?
Play it.
The macro expands to something like:
enum Type {This, That }
fn match_it(t: Type, b: Box<Object>) {
let p = match t {
Type::This => mem::transmute::<Box<Object>, Box<This>>(b),
Type::That => mem::transmute::<Box<Object>, Box<That>>(b),
}
}
What's the type of p? Depending on something at runtime, the compile-time type must be different; this doesn't make sense in a statically typed language like Rust.
I suggest looking into std::any, which seems similar to what you might be trying to do.
As an alternative, maybe you want runtime casting using Box<Any>:
use std::any::Any;
struct N(isize);
struct S(String);
fn main() {
let mut v: Vec<Box<Any>> = Vec::new();
v.push(Box::new(N(17)));
v.push(Box::new(S("foo".to_string())));
let s = v.pop().unwrap().downcast::<S>().unwrap();
let n = v.pop().unwrap().downcast::<N>().unwrap();
println!("Extracted {} and {}", s.0, n.0);
}
Play link