Trying to export a macro from module. Macro generates structure implementing some traits defined in module. Is there a way to get macro without importing that traits manually?
// src/lib.rs
#![crate_name="macro_test"]
#![crate_type="lib"]
#![crate_type="rlib"]
pub trait B<T> where T: Copy {
fn new(x: T) -> Self;
}
#[macro_export]
macro_rules! test {
( $n:ident ) => {
struct $n<T> where T: Copy {
x: T
}
impl<T> B<T> for $n<T> where T: Copy {
fn new(x: T) -> Self {
$n { x: x }
}
}
}
}
// tests/test_simple.rs
#[macro_use]
extern crate macro_test;
test!(Test);
#[test]
fn test_macro() {
let a = Test::<i32>::new(1);
}
In that case I get an error:
<macro_test macros>:2:54: 2:61 error: use of undeclared trait name `B` [E0405]
<macro_test macros>:2 struct $ n < T > where T : Copy { x : T } impl < T > B < T > for $ n < T >
If I rewrite the trait implementation with $crate variable:
impl<T> $crate::B<T> for $n<T> where T: Copy {
error message changes to next:
tests\test_simple.rs:8:13: 8:29 error: no associated item named `new` found for type `Test<i32>` in the current scope
tests\test_simple.rs:8 let a = Test::<i32>::new(1);
^~~~~~~~~~~~~~~~
tests\test_simple.rs:8:13: 8:29 help: items from traits can only be used if the trait is in scope; the following trait is implemented but not in scope, perhaps add a `use` for it:
tests\test_simple.rs:8:13: 8:29 help: candidate #1: use `macro_test::B`
Why does it happen?
Because you can't call trait methods without useing the trait. That's got nothing to do with macros—it's just a standard rule in Rust.
Maybe you want the macro to generate an inherent impl instead? i.e.
impl<T> $n<T> where T: Copy {
pub fn new(x: T) -> Self {
$n { x: x }
}
}
instead of what you have currently.
Related
A pattern I use frequently when writing Python is using class-only designs. I'll create an abstract base class, consisting of only classmethod and staticmethod methods. From this base, I can derive subclasses with specialized behavior for these methods. Since each of these subclasses have the same interface, and don't have any instance methods, I can pass around the classes themselves to client code, as opposed to instances of those classes.
As a simple, yet real/relevant example, here's how I would tackle needing to support multiple file formats for config data:
import abc
import yaml
import json
class ConfigReader(abc.ABC):
#classmethod
def from_file_path(cls, path: str):
with open(path) as fo:
return cls.from_string(fo.read())
#classmethod
#abc.abstractmethod
def from_string(cls, s: str):
pass
class YamlConfigReader(ConfigReader):
#classmethod
def from_string(cls, s: str):
return yaml.load(s)
class JsonConfigReader(ConfigReader):
#classmethod
def from_string(cls, s: str):
return json.loads(s)
Client code can then use the classes themselves in lieu of instances of those classes to do config parsing:
import typing as tp
def read_config_file(path: str, config_reader: tp.Type[ConfigReader]):
return config_reader.from_file_path(path)
print(read_config_file('config.yaml', YamlConfigReader))
print(read_config_file('config.json', JsonConfigReader))
I'm trying to do something similar to the above in Rust, but I seem to be running into issues. My initial approach used traits and associated methods:
use std::path::Path;
use std::fs::File;
use std::io::Read;
use std::io::Error;
pub trait ConfigReader {
fn from_str<S: AsRef<str>>(s: S) -> Result<String, Error>;
fn from_file<P: AsRef<Path>>(p: P) -> Result<String, Error> {
let p = p.as_ref();
let mut f = File::open(p)?;
let mut buffer = String::new();
f.read_to_string(&mut buffer)?;
Self::from_str(buffer)
}
}
pub struct YamlConfigReader;
pub struct JsonConfigReader;
impl ConfigReader for YamlConfigReader {
fn from_str<S: AsRef<str>>(_s: S) -> Result<String, Error> {
Ok("dummy".to_string())
}
}
impl ConfigReader for JsonConfigReader {
fn from_str<S: AsRef<str>>(_s: S) -> Result<String, Error> {
Ok("dummy".to_string())
}
}
fn read_config_file<P: AsRef<Path>>(p: P, config_reader: &ConfigReader) -> Result<String, Error> {
config_reader.from_file(p)
}
fn main() {}
(playground)
This gives me the error:
error[E0038]: the trait `ConfigReader` cannot be made into an object
--> src/main.rs:37:1
|
37 | fn read_config_file<P: AsRef<Path>>(p: P, config_reader: &ConfigReader) -> Result<String, Error> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `ConfigReader` cannot be made into an object
|
= note: method `from_str` has no receiver
= note: method `from_file` has no receiver
Is this sort of pattern possible in Rust? If not, what are some ways I can emulate this sort of behavior of decoupled, modular, stateless interfaces?
In Rust, you would treat a type parameter as, well, a type parameter:
fn read_config_file<P: AsRef<Path>, C: ConfigReader>(p: P) -> Result<String, Error> {
C::from_file(p)
}
This limits the dynamisms of such constructs.
When using ty in a macro, this works in nearly all cases I've tried.
However, it seems it cant be used to declare a new struct instance.
eg: $my_type { some_member: some_value }
A more comprehensive example
macro_rules! generic_impl {
($my_type:ty) => {
impl $rect_ty {
pub fn flip(&self) -> $my_type { self.some_method() }
pub fn swap(&self, &other: $my_type) -> { self.some_swap_method(other) }
// so far so good!
// now our troubles start :(
pub fn create(&self) -> $my_type {
return $my_type { foo: 1, bar: 2, baz: 2 };
// ^^^^^^^^ this fails!!!
}
}
}
}
// example use
generic_impl(MyStruct);
generic_impl(MyOtherStruct);
The error is:
error: expected expression, found `MyStruct`
Changing the ty to an expr means I can't use impl $my_type.
Besides passing in 2x arguments, one a ty the other an expr:
Is there a way to construct a struct based on a ty argument to a macro?
No, not with ty.
The simple fix is to capture an ident instead, which is valid in both contexts. If you need something more complex than a simple identifier, then you're probably going to need to capture the name (for construction) and the type (for other uses) separately and specify them both on use.
I have protocol and his implementation written in Swift:
protocol P {
}
struct A: P {
}
Protocol is used as generic type for some function:
func foo<T: P>(param: T) {
}
func foo() {
foo(param: A())
}
Until now everything works properly. But I would like to set A() as a default parameter of given function:
func foo<T: P>(param: T = A()) {
}
Unfortunately with following error:
Default argument value of type 'A' cannot be converted to type 'T'.
Or
func foo<T: P>(param: T = A() as P) {
}
,
let a: P = A()
func foo<T: P>(param: T = a) {
}
Returns:
Default argument value of type 'P' cannot be converted to type 'T'
Or
func foo<T: P>(param: T = A() as T) {
}
Returns:
'A' is not convertible to 'T'; did you mean to use 'as!' to force downcast?
What I'm doing wrong? Where is the problem?
I do not want to use force cast like this:
func foo<T: P>(param: T = A() as! T) {
}
Thank you in advance.
You're trying to enforce a non-generic default argument in a generic function: you should probably think over what you're trying to achieve here.
For the sake of the discussion, you could include an attempted cast of A() to T in your function signature, but you'd need to change the argument type to optional to allow failed conversion (nil), e.g.
func foo<T: P>(param: T? = (A() as? T)) { }
A more sound alternative is including - in addition to your generic function - a concrete non-generic function for instances where T is A (concrete functions will take precedence over generic ones), in which case you can include the default argument of A() in the function signature of the concrete function. E.g.
protocol P { }
struct A: P { }
extension Int: P { }
func foo<T: P>(param: T) { print("called generic foo") }
func foo(param: A = A()) { print("called A specific foo") }
foo() // called A specific foo (making use of default arg)
foo(A()) // called A specific foo
foo(1) // called generic foo
Note that the non-generic foo is called even though A conforms to P (A could've made use of the generic foo): there's no conflict here as the concrete function takes precedence.
If you, on the other hand, just want your generic function to allow calling without the single argument (i.e., making use of a default argument), you can include a blueprint of a simple initializer in P, allowing you to initialize an instance of the generic type as default argument; see #Sulthan:s answer.
The only thing you need is to add a requirement for an initializer to the protocol:
protocol P {
init()
}
struct A: P {
var x: Int
init() {
x = 10
}
}
func foo<T: P>(param param: T = T()) {
}
However, you will have another problem. The type of the passed parameter decides the type of the generic so you will have to specify the generic type somehow else.
I'm trying to implement a macro which implements the Add trait for a struct, like so:
macro_rules! implement_add {
($t:ty) => {
impl std::ops::Add for $t {
type Output = $t;
fn add(self, rhs: $t) -> $t {
$t(self.0 + rhs.0) // error on this line
}
}
}
}
pub struct Length(f64);
implement_add!(Length);
fn main() {}
However, this gives an error on the indicated line:
<anon>:6:17: 6:19 error: unexpected token: `Length`
<anon>:6 $t(self.0 + rhs.0) // error on this line
^~
This makes no sense to me. Especially since, if I replace $t there with Length, it compiles fine. Am I doing something wrong in my macro?
Playground: http://is.gd/EIEKub
You've stumbled on a subtle bit of Rust's type system. Length is a type, but Length() is a function. These exist in different namespaces.
One work around is to extend your macro to accept a type and a function:
macro_rules! implement_add {
($t:ty, $c:ident) => {
impl std::ops::Add for $t {
type Output = $t;
fn add(self, rhs: $t) -> $t {
$c(self.0 + rhs.0) // error on this line
}
}
}
}
pub struct Length(f64);
implement_add!(Length, Length);
fn main() {}
If I defined some enum and wanted to create a parser from string to that type, is there something better than just:
impl TheType {
fn from_str(s: &str) -> TheType {
// ...
}
}
The right way for converting from a string / parsing text is to implement the FromStr trait. For the example from the question it would look like this:
use std::str::FromStr;
enum Failure {
ReasonOne,
ReasonTwo,
}
impl FromStr for TheType {
type Err = Failure;
fn from_str(s: &str) -> Result<TheType, Self::Err> {
unimplemented!()
}
}
For generic conversion that cannot fail, you should implement the std::convert::From trait:
use std::convert::From;
#[derive(PartialEq, Eq, Debug)]
enum MyEnum {
One,
Two,
Many(i64),
}
impl From<i64> for MyEnum {
fn from(val: i64) -> Self {
match val {
1 => MyEnum::One,
2 => MyEnum::Two,
_ => MyEnum::Many(val),
}
}
}
fn main() {
assert_eq!(MyEnum::from(1), MyEnum::One);
assert_eq!(MyEnum::from(2), MyEnum::Two);
assert_eq!(MyEnum::from(3), MyEnum::Many(3));
}
Conveniently, implementing From also automatically implements Into:
let one: MyEnum = 1.into(); assert_eq!(one, MyEnum::One);
let two: MyEnum = 2.into(); assert_eq!(two, MyEnum::Two);
let many: MyEnum = 3.into(); assert_eq!(many, MyEnum::Many(3));
For potentially failing conversion, you should implement std::convert::TryFrom instead. It's only available in Rust 1.34 and up though, before these versions you can use the implementation in the conv crate.