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 })
}
}
Related
I'm getting blocked on what I think it's a simple problem. I'm still learning Rust, and I want to do the following:
I want to create an async trait (using async-trait) that will instantiate a DB connection instance and it will return the struct that is implementing that trait.
mongo.rs
#[async_trait]
pub trait DB {
async fn init<T, E>() -> Result<T, E>;
}
Then: favorites.rs (See the implementation of the DB trait down below)
use async_trait::async_trait;
use mongodb::Collection;
use rocket::form::FromForm;
use rocket::serde::ser::StdError;
use serde::{Deserialize, Serialize};
use std::error::Error;
use uuid::Uuid;
pub struct FavoritesDB {
collection: Collection<Favorite>,
}
#[derive(Debug)]
pub enum FavoritesError {
UnknownError(Box<dyn Error>),
}
// Conflicts with the one down below
// impl From<Box<dyn Error>> for FavoritesError {
// fn from(err: Box<dyn Error>) -> FavoritesError {
// FavoritesError::UnknownError(err)
// }
// }
impl From<Box<dyn StdError>> for FavoritesError {
fn from(err: Box<dyn StdError>) -> FavoritesError {
FavoritesError::UnknownError(err)
}
}
#[async_trait]
impl mongo::DB for FavoritesDB {
async fn init<FavoritesDB, FavoritesError>() -> Result<FavoritesDB, FavoritesError> {
let main_db = mongo::init::<Favorite>("Favorites").await?;
let db = FavoritesDB {
collection: main_db.collection,
};
Ok(db)
}
}
There are a list of problems with this:
1)
error[E0574]: expected struct, variant or union type, found type parameter `FavoritesDB`
--> src\db\favorites.rs:41:18
|
41 | let db = FavoritesDB {
| ^^^^^^^^^^^ not a struct, variant or union type
|
help: consider importing this struct instead
I've tried implementing From<Box<dyn tdError>> manually but it conflicts with what I have.
error[E0277]: `?` couldn't convert the error to `FavoritesError`
--> src\db\favorites.rs:40:65
|
40 | let main_db = mongo::init::<Favorite>("Favorites").await?;
| ^ the trait `From<Box<dyn StdError>>` is not implemented for `FavoritesError`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= note: required because of the requirements on the impl of `FromResidual<Result<Infallible, Box<dyn StdError>>>` for `Result<FavoritesDB, FavoritesError>`
note: required by `from_residual`
--> C:\Users\asili\.rustup\toolchains\nightly-2021-11-15-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\core\src\ops\try_trait.rs:339:5
|
339 | fn from_residual(residual: R) -> Self;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider further restricting this bound
|
39 | async fn init<FavoritesDB, FavoritesError + std::convert::From<std::boxed::Box<dyn std::error::Error>>>() -> Result<FavoritesDB, FavoritesError> {
| ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Some errors have detailed explanations: E0277, E0282, E0574.
For more information about an error, try `rustc --explain E0277`.
Just for more context, here's the DB struct and impl (Currently connecting to a local MongoDB) included in mongo.rs
pub struct Database<T> {
client: mongodb::Database,
pub collection: Collection<T>,
}
impl<T> Database<T> {
pub async fn init() -> Result<mongodb::Database, Box<dyn Error>> {
let mut client_options = ClientOptions::parse("mongodb://localhost:27017").await?;
client_options.app_name = Some("My App".to_string());
// Get a handle to the deployment.
let client = Client::with_options(client_options)?;
let db = client.database("rust-svelte");
return Ok(db);
}
}
pub async fn init<T>(collection: &str) -> Result<Database<T>, Box<dyn Error>> {
let client = Database::<T>::init().await?;
let collection = client.collection::<T>(collection);
let db = Database { client, collection };
Ok(db)
}
I've been searching for a few days over SO and the Rust community and my Google-Rust-Fu isn't good enough to spot what's the problem. Any ideas?
You've declared init to take 2 generic parameters: T and E.
This means that the code that calls init has to provide the concrete types to fill in those parameters. For example, if someone was using your library, it would be totally feasible for them to write init::<i64, ()>(), and your code should deal with that.
Because of that, when you define your impl DB for FavouritesDB, you write this:
async fn init<FavoritesDB, FavoritesError>() -> Result<FavoritesDB, FavoritesError>
This is no different to writing:
async fn init<T, E>() -> Result<T, E>
you've just given the type parameters different names that happen to match a struct that you're probably trying to use.
A better pattern might be an associated type. Instead of the caller deciding what the concrete types are (as is the case with generics), with associated types, the implementation of the trait on the type sets the type.
This is common with things like Iterator. Iterator has no generic parameters, but a single associated type Item. This is because it wouldn't make sense to be able to impl Iterator<String> for MyStruct and impl Iterator<i64> for MyStruct at the same time. Instead, we want to implement Iterator for a type once, and that implementation carries with it the definition of the types it expects.
So something like this (I've omitted the async-ness for brevity since it doesn't seem to be a factor here):
trait DB {
type InitOk;
type InitErr;
fn init() -> Result<Self::InitOk, Self::InitErr>;
}
impl Db for FavouritesDB {
type InitOk = FavouritesDB;
type InitErr = FavouritesError;
fn init() -> Result<Self::InitOk, Self::InitErr> {
// now you can reference FavouritesDB the struct, rather than the generic parameter
}
}
I'd also add you may want to not have the InitOk type, and just return Self, but that's up to you if you think you might want a struct to be able to create a different type.
For part 2, Rust assumes nothing (other than Sized) about generic parameters. If you want Rust to force a generic to have some property, you have to add a bound.
The compiler is telling you here that it can't use the ? operator to convert automatically, because it doesn't know that your error type has a From<Box<dyn Error>> implementation.
If you know that every error type is going to implement that, you can add it as a bound on the associated type, like this:
trait DB {
type InitOk;
type InitErr: From<Box<dyn Error>>;
// ...
}
How does the the <From> trait know which type to convert to in different contexts if it's implemented for more than one type?
For example, i have three types that have some mutual conversions implemented. On Character i implement From for both Token and AminoAcid. Yet, when i call .into() in the hashmap i don't have to specify which type is required by the map's type definition. How is the trait aware of the context?
#[derive(Debug)]
struct Coordinate{
x:f64, y:f64, z:f64
}
#[derive(Debug)]
enum AminoAcid {
VAL, GLN ,ARG,...
}
#[derive(Debug)]
enum Token {
Coordinate(Coordinate),
AminoAcid(AminoAcid),
Character(Character)
}
impl From<Coordinate> for Token {
fn from(coord: Coordinate) -> Self {
Self::Coordinate(coord)
}
}
impl From<AminoAcid> for Token {
fn from(aa: AminoAcid) -> Self {
Self::AminoAcid(aa)
}
}
// ------------------------------------------------- <<<<
impl From<Character> for Token {
fn from(c: Character) -> Self {
Self::Character(c)
}
}
impl From<Character> for AminoAcid {
fn from(c: Character) -> Self {
Self::ALA
}
}
// ------------------------------------------------- <<<<
lazy_static! {
static ref HASHMAP: HashMap<&'static str, Token> = { // Here the value type is Token
let mut m = HashMap::new();
m.insert("ALA", Character::Second.into());
m
};
static ref HASHMAP: HashMap<&'static str, AminoAcid> = { // Here the value type is AminoAcid
let mut m = HashMap::new();
m.insert("ALA", Character::Second.into());
m
};
}
It's because Rust can deduce/infer types.
For example
fn f1(arg: u8) {
println!("u8 in f1: {}", arg)
}
fn f2(arg: i64) {
println!("i64 in f2: {}", arg)
}
fn main() {
let a = 12;
let b = 23;
f1(a);
f2(b);
// f1(b); // expected `u8`, found `i64`
// f2(a); // expected `i64`, found `u8`
}
a and b are declared the same way; at this point, the compiler just knows they are some kind of integer.
At the first call of f1(), since the expected argument is an u8, Rust deduces that a is actually an u8.
The same goes for b deduced as an i64 at the first usage of f2().
Of course, if after that we try to make the compiler deduce other types for these variables, it fails.
In your case, you declare your static bindings as hashmaps of a specific type: Token in one case, AminoAcid in the other.
Then, the brace used to initialise such a binding have to match this type; the type of the resulting expression (m) is deduced accordingly.
The way we initialise m expects the correct value type.
Consequently, the version of into() providing this type is chosen.
Because you defined the type of HASHMAP, the compiler infers which type is needed, since if it converted into the other type it would not compile. https://doc.rust-lang.org/rust-by-example/types/inference.html
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);
}
The following code works (cargo +nightly run) fine:
fn main() {
let res: Result<(), String> = Err(String::from("hi"));
println!("{}", res.map_err(shout).unwrap_err())
}
fn shout(s: String) -> String {
s.to_ascii_uppercase()
}
Clippy (cargo +nightly clippy) spits out a (justified) warning:
warning: this argument is passed by value, but not consumed in the function body
--> src/main.rs:6:13
|
6 | fn shout(s: String) -> String {
| ^^^^^^ help: consider changing the type to: `&str`
Changing the code to the suggested version
fn shout(s: &str) -> String {
s.to_ascii_uppercase()
}
results in a compiler error:
error[E0631]: type mismatch in function arguments
--> src/main.rs:3:24
|
3 | println!("{}", res.map_err(shout).unwrap_err())
| ^^^^^^^ expected signature of `fn(std::string::String) -> _`
...
6 | fn shout(s: &str) -> String {
| --------------------------- found signature of `for<'r> fn(&'r str) -> _`
What is the right way to react? Sure, I could simply do #![cfg_attr(feature="clippy", allow(needless_pass_by_value))] but this feels wrong to me. Is there a way to use map_err with the version of shout taking a reference?
The best you can do is use a full closure:
res.map_err(|x| shout(&x)).unwrap_err()
Your original form has two steps needed to be able to work:
It needs to take the argument to the closure and convert it to a reference.
It needs to convert the &String to a &str.
Additionally, it needs to do both of those while the value is in scope, so that it doesn't end up with a dangling reference. Neither of these are things the "short" form of closures handles right now — the types must match exactly.
If you really wanted to avoid the closure, you can for this specific case:
res.as_ref().map_err(String::as_str).map_err(shout).unwrap_err()
// ^~~~~~ ^~~~~~~~~~~~~~
// | |
// | |- Convert `&String` to `&str`
// |
// |- Get a reference (`&String`)
I actually argued for the ability for your original code to work as part of the ergonomics initiative, but it did not seem to gain traction.
Like many problems in programming, you can "solve" this by adding more abstraction. Here, we introduce a trait to embody the concept of "an error that can be shouted":
fn main() {
let e1 = Err::<(), _>(String::from("hi"));
println!("{}", e1.map_err(ShoutyError::shout).unwrap_err());
let e2 = Err::<(), _>(42);
println!("{}", e2.map_err(ShoutyError::shout).unwrap_err());
}
trait ShoutyError {
fn shout(self) -> String;
}
impl ShoutyError for String {
fn shout(self) -> String {
self.to_ascii_uppercase()
}
}
impl ShoutyError for i32 {
fn shout(self) -> String {
format!("I YELL {}", self)
}
}
If you felt like you needed it, you could also have a wrapper function to keep the exact initial code:
fn shout<E: ShoutyError>(e: E) -> String {
e.shout()
}
I'd like to have a function adapt that takes one function f : &T -> U and returns a new function g : T -> U.
This is possible, but only in nightly Rust:
#![feature(conservative_impl_trait)]
fn adapt<F, T, U>(f: F) -> impl Fn(T) -> U
where
F: Fn(&T) -> U,
{
move |arg| f(&arg)
}
Unfortunately, it doesn't solve your problem because shout doesn't accept a &String and this would require str to be a Sized type.
The more verbose solution involves AsRef:
#![feature(conservative_impl_trait)]
fn adapt<F, T1, T2, U>(f: F) -> impl Fn(T1) -> U
where
F: Fn(&T2) -> U,
T1: AsRef<T2>,
T2: ?Sized,
{
move |arg| f(arg.as_ref())
}
I'm trying to apply some OOP but I'm facing a problem.
use std::io::Read;
struct Source {
look: char
}
impl Source {
fn new() {
Source {look: '\0'};
}
fn get_char(&mut self) {
self.look = 'a';
}
}
fn main() {
let src = Source::new();
src.get_char();
println!("{}", src.look);
}
Compiler reports these errors, for src.get_char();:
error: no method named get_char found for type () in the current
scope
and for println!("{}", src.look);:
attempted access of field look on type (), but no field with that
name was found
I can't find out what I've missed.
Source::new has no return type specified, and thus returns () (the empty tuple, also called unit).
As a result, src has type (), which does not have a get_char method, which is what the error message is telling you.
So, first things first, let's set a proper signature for new: fn new() -> Source. Now we get:
error: not all control paths return a value [E0269]
fn new() -> Source {
Source {look: '\0'};
}
This is caused because Rust is an expression language, nearly everything is an expression, unless a semicolon is used to transform the expression into a statement. You can write new either:
fn new() -> Source {
return Source { look: '\0' };
}
Or:
fn new() -> Source {
Source { look: '\0' } // look Ma, no semi-colon!
}
The latter being the more idiomatic in Rust.
So, let's do that, now we get:
error: cannot borrow immutable local variable `src` as mutable
src.get_char();
^~~
Which is because src is declared immutable (the default), for it to be mutable you need to use let mut src.
And now it all works!
Final code:
use std::io::Read;
struct Source {
look: char
}
impl Source {
fn new() -> Source {
Source {look: '\0'}
}
fn get_char(&mut self) {
self.look = 'a';
}
}
fn main() {
let mut src = Source::new();
src.get_char();
println!("{}", src.look);
}
Note: there is a warning because std::io::Read is unused, but I assume you plan to use it.