trait Print {
fn print(&self);
}
struct Printer {
p: Box<Print>,
}
fn main() {
let i = 1i32;
let s = String::from_str("Hello");
// let mp = make_printer!(i,s);
let mp = |i: i32, s: String| {
struct Foo {
i: i32,
s: String,
}
impl Foo {
fn new(i: i32, s: String) -> Foo {
Foo { i: i, s: s }
}
}
impl Print for Foo {
fn print(&self) {
println!("{} {}", self.i, self.s);
}
}
Printer { p: box Foo::new(i, s) }
};
let printer = mp(i, s);
printer.p.print();
}
I want to create the make_printer! macro which should expand to mp if I call it with make_printer!(i,s).
Is this is possible? I think the biggest issue here is that I need something like decltype from C++ so that I can extract the type from a variable. The struct could then look like:
#![feature(macro_rules)]
macro_rules! make_printer(
($($element:ident),*) => (
struct Foo{
$($element : decltype($element),)*
}
)
)
No, it's not possible to do it with just the expression identifiers. You need explicit types, e.g.
macro_rules! make_printer {
($($element: ident: $ty: ty),*) => {
struct Foo { $($element: $ty),* }
}
}
called like make_printer!(x: i32, y: String).
This is required for two, mostly orthogonal, reasons:
macros don't have access to any type information, they are just local syntactic transformations (e.g. it's not even possible to write a macro (either macro_rules! or procedural) that can tell if a given ident refers to a local variable).
Rust doesn't have a decltype equivalent.
Related
I am working on a macro that computes the number of fields on a struct which are of type None at runtime. I have an implementation that computes the number of fields for a struct. The source code is given below.
use std::rc::Rc;
use std::cell::Cell;
macro_rules! generate {
($name:ident {$($field:ident : $t:ty),+}) => {
struct $name { $($field: $t),+ }
impl $name {
fn field_count(&self) -> usize {
generate!(#count $($field),+)
}
}
};
(#count $t1:tt, $($t:tt),+) => { 1 + generate!(#count $($t),+) };
(#count $t:tt) => { 1 };
}
generate! { test1 { num: i32, s: Option<String> }}
generate! { test2 { num: i32, s: String }}
fn main() {
let s = test1 { num: 0, s: None };
println!("{}", s.field_count());
let s = test2 { num: 0, s: String::new() };
println!("{}", s.field_count());
}
The problem with this implementation is this returns the total number of fields in the
struct at compile time so for test1 and test1 the answer is always 2. I am looking for a way to modify the macro to compute fields that are of type "None" in the struct.
To distinguish Nones, we first need to distinguish Options. And this is little hard.
There are two ways to do that: syntactically or via the type system. Syntactically means that the macro will identify fields that have the type Option<T> (i.e. Option < type >). However, this requires a TT munching, will force you to parse the ty fragment type yourself (you cannot rely on it because you'll not be able to inspect into its composed tokens later), and as a bonus won't work if users will type-alias Option.
The type system approach is more robust. The idea is to create some trait, implemented for any type, with a method that for Option<T> will return 1 iff it is Some and for other types will always return 1. This sounds like a perfect use case for specialization (playground):
#![feature(min_specialization)]
pub trait OneIfSome {
fn one_if_some(&self) -> usize;
}
impl<T> OneIfSome for T {
default fn one_if_some(&self) -> usize { 1 }
}
impl<T> OneIfSome for Option<T> {
fn one_if_some(&self) -> usize { self.is_some() as usize }
}
macro_rules! generate {
($name:ident {$($field:ident : $t:ty),+}) => {
struct $name { $($field: $t),+ }
impl $name {
fn field_count(&self) -> usize {
generate!(#count self $($field,)*)
}
}
};
(#count $self:ident $first_field:ident, $($rest:ident,)*) => {
$crate::OneIfSome::one_if_some(&$self.$first_field)
+ generate!(#count $self $($rest,)*)
};
(#count $self:ident) => { 0 };
}
Unfortunately, specialization is unstable, and doesn't even have a clear path towards stabilization. Can we replace it with something stable?
And it turns out, yes! #dtolnay invented a technique called "autoref specialization" that is intended exactly for cases like that (specialization inside a macro). I won't explain how it works here (the linked article is a great read), but here is how you can apply it to your case (playground):
pub trait OneIfSomeGeneral {
fn one_if_some(&self) -> usize;
}
impl<T> OneIfSomeGeneral for &'_ T {
fn one_if_some(&self) -> usize { 1 }
}
pub trait OneIfSomeOption {
fn one_if_some(&self) -> usize;
}
impl<T> OneIfSomeOption for Option<T> {
fn one_if_some(&self) -> usize { self.is_some() as usize }
}
macro_rules! generate {
($name:ident {$($field:ident : $t:ty),+}) => {
struct $name { $($field: $t),+ }
impl $name {
fn field_count(&self) -> usize {
#[allow(unused_imports)]
use $crate::{OneIfSomeGeneral, OneIfSomeOption};
generate!(#count self $($field,)*)
}
}
};
(#count $self:ident $first_field:ident, $($rest:ident,)*) => {
(&$self.$first_field).one_if_some()
+ generate!(#count $self $($rest,)*)
};
(#count $self:ident) => { 0 };
}
(Note: There is a problem with the above code: if the field type has a method named one_if_some(), it will take priority. So choose an unambiguous name (__my_generate_macro__one_if_some(), perhaps?)).
I am trying to understand generics in Rust and have attempted to write a generic function that can multiple any struct that has the property foo by 10. When I use this code, I get the error, no field foo on type T.
struct A {
foo: i8,
bar: String,
}
struct B {
foo: i8,
}
fn go<T>(v: T) {
v.foo * 10
}
fn main() {
let a = A {foo: 1, bar: "bar".to_string()};
let b = B {foo: 2};
println!("{:?},{:?}", go(a), go(b))
}
How can I write a generic function that can accept any struct that implements a foo: i8 property?
Rust doesn't have properties in the way that JavaScript does, so you need to do things a little differently. You'll want to use a trait to expose the foo functionality as a method, and then implement that trait for A and B:
trait Foo {
fn foo(&self) -> i8;
}
struct A {
foo: i8,
bar: String,
}
impl Foo for A {
fn foo(&self) -> i8 {
self.foo
}
}
struct B {
foo: i8,
}
impl Foo for B {
fn foo(&self) -> i8 {
self.foo
}
}
fn go<T: Foo>(v: T) -> i8 {
v.foo() * 10
}
fn main() {
let a = A {
foo: 1,
bar: "bar".to_string(),
};
let b = B { foo: 2 };
println!("{:?},{:?}", go(a), go(b))
}
(Note that I also made go return a value above.)
This provides a Foo trait that exposes foo as a method, implements it for A and B, and then makes go require T to implement Foo. That's the easiest and most straightforward way to accomplish this goal.
I want to hold a PathBuf inside my structure:
use std::path::{Path, PathBuf};
struct Foo {
p: PathBuf,
}
impl Foo {
fn new(p: PathBuf) -> Foo {
Foo { p }
}
}
Something like this works fine for Foo::new(Path::new("a").join("b")), but I also want to support Foo::new(Path::new("a")):
fn main() {
Foo::new(Path::new("a").join("b"));
// Foo::new(Path::new("a"));
}
How should I do it? Is it possible to implement with one method, or should I use two methods? I know about P: AsRef<Path>, but looks like it requires additional copy in the case of
let p: PathBuf = Path::new("a").join("b");
let foo = Foo::new(p);
so it is not suitable for me.
Take a generic type that can be converted Into a PathBuf:
use std::path::{Path, PathBuf};
struct Foo {
p: PathBuf,
}
impl Foo {
fn new<P>(p: P) -> Foo
where
P: Into<PathBuf>,
{
Foo { p: p.into() }
}
}
fn main() {
Foo::new(Path::new("a").join("b"));
Foo::new(Path::new("a"));
Foo::new("a");
}
At the moment, implementing the std::ops::IndexMut trait on a type in Rust requires that I also implement the std::ops::Index trait as well. The bodies of these implementations end up being virtually identical. For example:
use std::ops::{Index, IndexMut};
enum IndexType {
A,
B,
}
struct Indexable {
a: u8,
b: u8,
}
impl Index<IndexType> for Indexable {
type Output = u8;
fn index<'a>(&'a self, idx: IndexType) -> &'a u8 {
match idx {
IndexType::A => &self.a,
IndexType::B => &self.b,
}
}
}
impl IndexMut<IndexType> for Indexable {
fn index_mut<'a>(&'a mut self, idx: IndexType) -> &'a mut u8 {
match idx {
IndexType::A => &mut self.a,
IndexType::B => &mut self.b,
}
}
}
fn main() {}
This works, and obviously for trivial types this isn't a serious problem, but for more complex types with more interesting indexing this quickly becomes laborious and error-prone. I'm scratching my head trying to find a way to unify this code, but nothing is jumping out at me, and yet I feel there has to/should be a way to do this without essentially having to copy and paste. Any suggestions? What am I missing?
Unfortunately, this cuts across a few things Rust really isn't good at right now. The cleanest solution I could come up with was this:
macro_rules! as_expr {
($e:expr) => { $e };
}
macro_rules! borrow_imm { ($e:expr) => { &$e } }
macro_rules! borrow_mut { ($e:expr) => { &mut $e } }
macro_rules! impl_index {
(
<$idx_ty:ty> for $ty:ty,
($idx:ident) -> $out_ty:ty,
$($body:tt)*
) => {
impl ::std::ops::Index<$idx_ty> for $ty {
type Output = $out_ty;
fn index(&self, $idx: $idx_ty) -> &$out_ty {
macro_rules! index_expr { $($body)* }
index_expr!(self, borrow_imm)
}
}
impl ::std::ops::IndexMut<$idx_ty> for $ty {
fn index_mut(&mut self, $idx: $idx_ty) -> &mut $out_ty {
macro_rules! index_expr { $($body)* }
index_expr!(self, borrow_mut)
}
}
};
}
enum IndexType { A, B }
struct Indexable { a: u8, b: u8 }
impl_index! {
<IndexType> for Indexable,
(idx) -> u8,
($this:expr, $borrow:ident) => {
match idx {
IndexType::A => $borrow!($this.a),
IndexType::B => $borrow!($this.b),
}
}
}
fn main() {
let mut x = Indexable { a: 1, b: 2 };
x[IndexType::A] = 3;
println!("x {{ a: {}, b: {} }}", x[IndexType::A], x[IndexType::B]);
}
The short version is: we turn the body of index/index_mut into a macro so that we can substitute the name of a different macro that, given an expression, expands to either &expr or &mut expr. We also have to re-capture the self parameter (using a different name) because self is really weird in Rust, and I gave up trying to make it work nicely.
If I have a small struct Test:
struct Test<T> { a: T }
And I wish, in a method of Test to refer to its full type:
impl<T> Test<T> {
fn new(a: T) -> Test<T> {
type X = Test::<T>;
println!("{}", std::intrinsics::type_id::<X>());
Test { a: a }
}
}
This fails with expected ident, found <, and the following fail too:
type X = Test;: wrong number of type arguments: expected 1, found 0 [E0243]
type X = Test<T>;: can't use type parameters from outer function; try using a local type parameter instead with a note use of undeclared type name T
Actually, it makes sense that the former two are rejected; the latter however is slightly more mysterious.
This came about in trying to implement an offset_of! macro: offset_of($T:ty, $field:ident); the macro works rather well, however ty does not accept Test<T> (but accept a parameter-less alias).
Is there any way to either:
craft an alias to the type of self, even in generics?
or, alternatively, make it so that the macro accepts Test<T> as a "type" argument?
Note: I would prefer a solution to the former, if possible, as aliases are really handy.
For reference, here is the offset_of macro I crafted:
macro_rules! offset_of(
($T:ty, $field:ident) => {
unsafe {
let exemplar: $T = std::mem::uninitialized();
let base: *const u8 = std::mem::transmute(&exemplar);
let attr: *const u8 = std::mem::transmute(&exemplar.$field);
std::mem::forget(exemplar);
(attr as isize) - (base as isize)
}
}
);
I may be misunderstanding you, but there already is an alias for the type of self — Self:
#![feature(core)]
struct Test<T> { a: T }
impl<T> Test<T> {
fn new(a: T) -> Test<T>
where T: 'static
{
println!("{}", unsafe { std::intrinsics::type_id::<Self>() });
Test { a: a }
}
}
fn main() {}
I had to add the feature gates, make T 'static to satisfy type_id, and add an unsafe block. I hope that none of that seems suspicious. This seems to work with your alias, as well:
macro_rules! offset_of(
($T:ty, $field:ident) => {
unsafe {
let exemplar: $T = std::mem::uninitialized();
let base: *const u8 = std::mem::transmute(&exemplar);
let attr: *const u8 = std::mem::transmute(&exemplar.$field);
(attr as isize) - (base as isize)
}
}
);
struct Test<T> { a: T, b: T, c: T }
impl<T> Test<T> {
fn new(a: T) -> Test<T>
where T: Copy
{
println!("{}", offset_of!(Self, a));
println!("{}", offset_of!(Self, b));
println!("{}", offset_of!(Self, c));
Test { a: a, b: a, c: a }
}
}
fn main() {
Test::new(1u16);
}