I have a problem when I use macro_rules!.
I defined a enum Test and impl fmt for the enum.
use core::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Test {
Foo(String),
Bar,
}
impl fmt::Display for Test {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Test::Foo(id) => write!(f, "Foo({})", id),
Test::Bar => write!(f, "Bar"),
}
}
}
Then I defined a macro print_test!.
macro_rules! print_test {
($test:pat) => {
println!("the Test is {}", $test);
};
}
However I got an error .
error: expected expression, found `Bar`
--> src/main.rs:53:36
|
53 | println!("the Test is {}", $test);
| ^^^^^ expected expression
...
57 | print_test!(Bar);
| ----------------- in this macro invocation
I'm a new Rustacean. And I really don't know why this happened.
update
I have already import the enum variants in a global scope.And following is the complete code
mod test {
use core::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Test {
Foo(String),
Bar,
}
impl fmt::Display for Test {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Test::Foo(id) => write!(f, "Foo({})", id),
Test::Bar => write!(f, "Bar"),
}
}
}
}
use test::Test::*;
macro_rules! print_test {
($test:pat) => {
println!("the Test is {}", $test);
};
}
fn main() {
let a=String::from ("test");
print_test!(Bar);
}
Just change the pat to expr can resolve the problem.
macro_rules! print_test {
($test:expr) => {
println!("{}",$test);
};
}
Related
I am trying to figure out how the error enum works to wrap std::error:Error by passing in bad input for Url. I wish to wrap url::ParseError.
use std::error::Error;
use std::fmt::Display;
#[derive(Debug)]
pub enum WordCount {
UrlError(url::ParseError),
}
impl Display for WordCount {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
WordCount::UrlError(e) => {
write!(f, "{}", e)
},
}
}
}
impl std::error::Error for WordCount {}
impl From<url::ParseError> for WordCount {
fn from(err: url::ParseError) -> Self {
WordCount::UrlError(err)
}
}
pub fn run() -> Result<String, WordCount> {
let args: Vec<String> = env::args().collect();
let arg = &args[1].as_ref();
let domain = Url::parse(arg);
match domain {
Ok(_) => {}
Err(err) => return Err(WordCount::UrlError(err)),
};
Ok("".to_owned())
}
If I replace the following line:
Err(err) => return Err(WordCount::UrlError(err)), in the fn run()
with: panic!("some string") e.g
match domain {
Ok(domain) => println!("happy domain"),
Err(err) => panic!("panicking"),
};
Then I can see an error print to the terminal. But not with the implementation, nothing prints to stderr even if I have implemented Display.
Just returning an error doesn't implicetly print it. You could make your main function to return the same error, then your program will report the message at the end of execution. As an example:
use url::Url;
use std::error::Error;
use std::fmt::Display;
#[derive(Debug)]
pub enum CrawlerError {
UrlError(url::ParseError),
}
impl Display for CrawlerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CrawlerError::UrlError(e) => {
write!(f, "{}", e)
},
}
}
}
impl std::error::Error for CrawlerError {}
impl From<url::ParseError> for CrawlerError {
fn from(err: url::ParseError) -> Self {
CrawlerError::UrlError(err)
}
}
pub fn run() -> Result<String, CrawlerError> {
let domain = Url::parse("foo/bar");
match domain {
Ok(_) => Ok("".to_string()),
Err(err) => Err(CrawlerError::UrlError(err)),
}
}
fn main() -> Result<(), CrawlerError> {
run()?;
Ok(())
}
Playground
Results:
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 1.25s
Running `target/debug/playground`
Error: UrlError(RelativeUrlWithoutBase)
I have a custom Error that I use to wrap around several standard Rust errors that looks like this:
#[derive(Debug)]
pub struct MyError {
details: String,
}
impl MyError {
fn new(msg: &str) -> MyError {
dbg!(msg);
MyError {
details: msg.to_string(),
}
}
}
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.details)
}
}
impl From<std::io::Error> for MyError {
fn from(err: std::io::Error) -> Self {
MyError::new(&err.to_string())
}
}
impl From<std::string::FromUtf8Error> for MyError {
fn from(err: std::string::FromUtf8Error) -> Self {
MyError::new(&err.to_string())
}
}
Right now, I simply bubble up the the error message into my MyError. However, I'd also like to create a method that returns what the underlying Error type was so that I can match on it with an Enum or something at a later point in time. How can I add new method to my Error that outputs the underlying error type? I see that Error.kind() may be what I'm looking for, but I couldn't get it to work.
Here's the implementation of what I mentioned in the comments, and below is how you would use it and match it: Playground
use std::fmt;
#[derive(Debug)]
pub enum MyError {
FromUtf8(std::string::FromUtf8Error),
Io(std::io::Error),
Generic(String),
}
impl MyError {
fn new(msg: &str) -> Self {
dbg!(msg);
MyError::Generic(msg.to_string())
}
}
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use MyError::*;
match self {
FromUtf8(e) => write!(f, "{}", e),
Io(e) => write!(f, "{}", e),
Generic(e) => write!(f, "{}", e),
}
}
}
impl From<std::io::Error> for MyError {
fn from(err: std::io::Error) -> Self {
MyError::Io(err)
}
}
impl From<std::string::FromUtf8Error> for MyError {
fn from(err: std::string::FromUtf8Error) -> Self {
MyError::FromUtf8(err)
}
}
fn main() {
let e1 = MyError::new("Oops");
println!("{}", e1);
use MyError::*;
match e1 {
FromUtf8(e) => println!("UTF8 error"),
Io(e) => println!("IO error"),
Generic(e) => println!("Just an error"),
}
}
The program below compiles and runs if Rc is replaced with Box. Why doesn't it compile when using reference counting? This is a question about the difference between Rc<T> and Box<T>.
use std::rc::Rc;
#[derive(Debug, Clone)]
pub enum ILst {
Nil,
Cons(i32, Rc<ILst>),
}
impl ILst {
pub fn new() -> Self {
ILst::Nil
}
pub fn cons(self, item: i32) -> Self {
ILst::Cons(item, Rc::new(self))
}
pub fn car(&self) -> Option<i32> {
match *self {
ILst::Cons(u, ref _v) => Some(u),
ILst::Nil => None,
}
}
pub fn cdr(&self) -> Self {
match *self {
ILst::Cons(_u, ref v) => *v.clone(),
ILst::Nil => ILst::Nil,
}
}
}
fn main() {
let list = ILst::new().cons(17).cons(29);
let rest = list.cdr();
println!("list = {:?}", rest);
}
error[E0507]: cannot move out of borrowed content
--> src/main.rs:27:38
|
27 | ILst::Cons(_u, ref v) => *v.clone(),
| ^^^^^^^^^^ cannot move out of borrowed content
The solution seems to be to replace
*v.clone()
with
Rc::deref(v).clone()
and then add the line
use::ops::Deref;
to the beginning of the program.
I have some problems with a generic implementation of a method:
use std::collections::BTreeMap;
use global::entity::EntityId;
struct simple_system<T> {
list_sum: BTreeMap<EntityId, T>,
list_com: BTreeMap<EntityId, Vec<T>>,
}
impl<T> simple_system<T> {
pub fn new() -> simple_system<T> {
simple_system {
list_sum: BTreeMap::new(),
list_com: BTreeMap::new(),
}
}
pub fn addComponent(&mut self, id: EntityId, comp: T) {
self.list_com.entry(id).or_insert_with(Vec::new).push(comp);
match self.list_sum.get_mut(&id) {
Some(v) => *v = *v + *comp,
None => self.list_sum.insert(id, comp),
}
}
}
with the following errors.
error[E0614]: type `T` cannot be dereferenced
--> src/main.rs:20:34
|
20 | Some(v) => *v = *v + *comp,
| ^^^^^
error[E0369]: binary operation `+` cannot be applied to type `T`
--> src/main.rs:20:29
|
20 | Some(v) => *v = *v + *comp,
| ^^^^^^^^^^
|
= note: an implementation of `std::ops::Add` might be missing for `T`
I don't know what I have to change to get it to work. I use it with u32 type so it should have an + operator.
The Rust generics systems doesn't work the way C++ templates do: in C++ the compiler doesn't check whether the code actually compiles with any type in advance.
Rust makes sure the function compiles with any type that fulfills the listed requirements (called trait bounds). The compiler already told you what is missing: std::ops::Add might be missing for T, so ask for it:
impl<T: Add<Output = T>> simple_system<T> { /* … */ }
This will not fix everything; your code has other issues as well.
And here the Solution:
At first you should write a working non generic (c++ template) version and then evolve it to a generic version.
use std::collections::BTreeMap;
#[derive(Debug)]
struct SumUpSystem {
list_sum: BTreeMap<u64, i32 >,
list_com: BTreeMap<u64, Vec<i32> >
}
impl SumUpSystem {
pub fn new() -> SumUpSystem {
SumUpSystem {
list_sum: BTreeMap::new(),
list_com: BTreeMap::new()
}
}
pub fn add_component(&mut self, id: u64, comp: i32) {
self.list_com.entry(id).or_insert_with(Vec::new).push(comp);
let mut insert = false;
match self.list_sum.get_mut(&id) {
Some(x) => *x = *x + comp,
None => insert = true
}
if (insert) {
self.list_sum.insert(id, comp);
}
}
pub fn sum(& self, id: u64) -> i32 {
if let Some(x) = self.list_sum.get(&id) {
*x
} else {
panic!("Invalid id: Not in system!");
}
}
}
#[cfg(test)]
mod test {
use super::*;
macro_rules! init_test {
($v:ident) => (let mut $v : SumUpSystem = SumUpSystem::new(););
}
#[test]
fn add_component() {
init_test!(s);
s.add_component(1, 13);
assert_eq!(s.sum(1), 13);
s.add_component(1, 26);
assert_eq!(s.sum(1), 13 + 26);
}
}
The generic (c++ template). You should read the Trait section of the Rust Documentation to understand how/why it works.
use std::collections::BTreeMap;
use std::ops::Add;
#[derive(Debug)]
struct SumUpSystem<T> {
list_sum: BTreeMap<u64, T >,
list_com: BTreeMap<u64, Vec<T> >
}
impl <T: Add<Output=T> + Clone> SumUpSystem<T> {
pub fn new() -> SumUpSystem<T> {
SumUpSystem {
list_sum: BTreeMap::new(),
list_com: BTreeMap::new()
}
}
pub fn add_component(&mut self, id: u64, comp: &T) {
self.list_com.entry(id).or_insert_with(Vec::new).push(comp.clone());
let mut insert = false;
match self.list_sum.get_mut(&id) {
Some(x) => *x = x.clone() + comp.clone(),
None => insert = true
}
if insert {
self.list_sum.insert(id, comp.clone());
}
}
pub fn sum(& self, id: u64) -> T {
if let Some(x) = self.list_sum.get(&id) {
x.clone()
} else {
panic!("Invalid id: Not in system!");
}
}
}
#[cfg(test)]
mod test {
use super::*;
macro_rules! init_test {
($v:ident) => (let mut $v : SumUpSystem<i32> = SumUpSystem::new(););
}
#[test]
fn add_component() {
init_test!(s);
s.add_component(1, &13i32);
assert_eq!(s.sum(1), 13i32);
s.add_component(1, &26i32);
assert_eq!(s.sum(1), 39i32);
}
}
I wrote a very simple macro which should implement a wrapper for a C struct:
macro_rules! features{
($struct_name: ident, $from_type: ident, $($name: ident => $vk: ident,)+) => {
#[derive(Debug, Copy, Clone)]
pub struct $struct_name{
$(
pub $name: bool,
)+
}
impl $struct_name{
pub fn empty() -> $struct_name{
$struct_name{
$(
$name: false,
)+
}
}
pub fn subset(&self, other: &Self) -> bool{
$((!self.$name | other.$name))&&+
}
}
//TODO: Probably just impl From with a cast?
impl From<vk::$from_type> for $struct_name{
fn from(features: vk::$from_type) -> $struct_name {
$struct_name{
$(
$name: features.$vk != 0,
)+
}
}
}
impl From<$struct_name> for vk::$from_type {
fn from(features: $struct_name) -> vk::$from_type {
vk::$from_type{
$(
$vk: features.$name as u32,
)+
}
}
}
impl fmt::Display for $struct_name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result{
$(
if self.$name {
try!(writeln!(f, "{},", stringify!($name)));
}
)+
writeln!(f, "")
}
}
}
}
I use it like this:
features!{
Features,
PhysicalDeviceFeatures,
robust_buffer_access => robustBufferAccess,
full_draw_index_uint32 => fullDrawIndexUint32,
image_cube_array => imageCubeArray,
....
}
This works, but I had to make a very ugly hack $from_type: ident and then I use vk::$from_type inside the macro. What I really want to do is:
features!{
Features,
vk::PhysicalDeviceFeatures,
robust_buffer_access => robustBufferAccess,
full_draw_index_uint32 => fullDrawIndexUint32,
image_cube_array => imageCubeArray,
....
}
Then I can fully supply the type, instead of relying on the ident being inside a hard coded module vk::.
The problem is that I can not do this with path or ty
$from_type{..}
Error:
error: expected expression, found `PhysicalDeviceFeatures`
-->
|
38 | $from_type{
| ^^^^^^^^^^
and
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
--> /home/maik/projects/ashlib/ash/src/feature.rs:38:27
|
38 | $from_type{
|
What symbol does $from_type need to be for this to work?
I created a shorter example to express the problem that I am having more clearly (Playground):
mod foo {
struct Foo{
i: i32
}
}
macro_rules! c_enum {
(
$name:ty
) => {
fn new() -> $name {
$name{
i: 42
}
}
}
}
c_enum!{
foo::Foo
}
fn main() {
}
I don't know why path or ty fails, I would file a bug against Rust. In the past I've worked around this by using the tt specifier, which matches almost everything.