How to make macro import/use struct in any place it's used? - rust

Suppose I have the following code inside a module:
struct A{
}
#[macro_export]
macro_rules! profile {
($variable_name:tt, $expression:expr) => {
let a = A{};
}
}
so it'd be in a/a.rs, to the side of a/mod.rs which exports a::A and also exports the macro.
The problem is that when I use this macro on other modules like b/b.rs, I'd have to use super::a::A before using the macro.
I could change let a = A{}; on the macro to let a = self::a::A{};. However, it'd not work on all modules, and certainly not for users of this library that use this library in their code.
How can I specify let a = something::something::A{}; in a way that it works anywhere inside my library as well for users of this lib?

In declarative macros specifically, you can use the $crate metavariable to reference the crate the macro is defined in, and then use the absolute path of the item. This will work both inside and outside of your library. For example, if your struct A is defined in your library at the path module_a::module_b::A, you would use:
struct A{
}
#[macro_export]
macro_rules! profile {
($variable_name:tt, $expression:expr) => {
let a = $crate::module_a::module_b::A{};
}
}
Here's the relevant section of the Rust reference guide.

Related

How to use `clippy::disallowed_method` with methods inside the same crate

I have some functions defined in my crate that I want to disallow the use of inside of the same, using clippy::disallowed_methods.
For example:
fn f() { ... }
fn g() {
f(); // Warning
}
I've tried the following clippy.toml
disallowed-methods = [
"crate::f"
]
But no warning shows up when I run cargo clippy.
I've also tried f, my_crate::f and ::my_crate::f, where my_crate is the name of the crate both functions are defined in, but none of them work.
I've tried it with other methods in external crates, such as std::vec::Vec::new and a warning successfully shows up.
Is there any way to make disallowed_methods work without moving the methods to another crate?

Defining a struct member with private module types

I've been using a bunch of modules that have a build() function which returns a struct. However, when I try to create my own "super" struct to bundle them together, I run into the error module `xxx` is private rustc(E0603). If there is a trait I can pass the individual variable as a parameter but cannot figure out how to define/box it up for a struct.
The current example of this I'm hitting is when creating a hyper client.
// Error due to privacy and cannot use the trait to define the member type
// Both the "hyper_rustls::connector" and "hyper::client::connect::http" modules are private.
struct SecureClient {
client: hyper::client::Client<
hyper_rustls::connector::HttpsConnector<hyper::client::connect::http::HttpConnector>>
}
// Works, but passing the client everywhere as an individual variable is not realistic.
fn use_client(client: hyper::client::Client<impl hyper::client::connect::Connect>) -> () {
()
}
let https_conn = hyper_rustls::HttpsConnector::new(4);
let client: hyper::client::Client<_, hyper::Body> = hyper::Client::builder().build(https_conn);
Being newish to Rust, I'm struggling to figure out what the proper jargon is for what I'm trying to do, let alone make it work. Links to any docs or code examples about this would be appreciated.
Thanks
I'm not sure what you want to do, but you can use the public re-export hyper_rustls::HttpsConnector instead of the private hyper_rustls::connector::HttpsConnector and the public re-export hyper::client::HttpConnector instead of the private hyper::client::connect::http::HttpConnector.
You can read about re-exports here: https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#re-exporting-names-with-pub-use

Can't use a neon JsArray: This function takes 3 parameters but 2 were supplied

I'm learning how to use neon, but I don't understand a thing. If I try to execute this code:
#[macro_use]
extern crate neon;
use neon::vm::{Call, JsResult};
use neon::mem::Handle;
use neon::js::{JsInteger, JsNumber, JsString, JsObject, JsArray, JsValue, Object, Key};
use neon::js::error::{JsError, Kind};
fn test(call: Call) -> JsResult<JsArray> {
let scope = call.scope;
let js_arr: Handle<JsArray> = try!(try!(call.arguments.require(scope, 1)).check::<JsArray>());
js_arr.set(0, JsNumber::new(scope, 1000));
Ok(js_arr)
}
register_module!(m, {
m.export("test", test)
});
I get this error when I call js_arr.set: This function takes 3 parameters but 2 were supplied.
I don't understand why since it's a JsArray. Even Racer tells me that the set method takes 2 parameters. No matter what, js_arr.set takes 3 parameters in this order: &mut bool, neon::macro_internal::runtime::raw::Local and neon::macro_internal::runtime::raw::Local.
What's happening? I can't understand how JsArray works.
As paulsevere says on a GitHub issue for Neon, import neon::js::Object. In addition, do not import Key, which also provides a set method:
#[macro_use]
extern crate neon;
use neon::vm::{Call, JsResult};
use neon::js::{Object, JsArray, JsInteger, JsObject, JsNumber};
fn make_an_array(call: Call) -> JsResult<JsArray> {
let scope = call.scope; // the current scope for rooting handles
let array = JsArray::new(scope, 3);
array.set(0, JsInteger::new(scope, 9000))?;
array.set(1, JsObject::new(scope))?;
array.set(2, JsNumber::new(scope, 3.14159))?;
Ok(array)
}
register_module!(m, {
m.export("main", make_an_array)
});
This creates a brand new array. If you'd like to accept an array as the first argument to your function and then modify it, this works:
#[macro_use]
extern crate neon;
use neon::vm::{Call, JsResult};
use neon::js::{Object, JsArray, JsInteger, JsUndefined};
use neon::mem::Handle;
fn hello(call: Call) -> JsResult<JsUndefined> {
let scope = call.scope;
let js_arr: Handle<JsArray> = call.arguments.require(scope, 0)?.check::<JsArray>()?;
js_arr.set(0, JsInteger::new(scope, 1000))?;
Ok(JsUndefined::new())
}
register_module!(m, {
m.export("hello", hello)
});
let js_arr: Handle<JsArray> makes it clear that js_arr is a Handle<JsArray> and Handle<T> has this method:
unsafe fn set(self, out: &mut bool, obj: Local, val: Local) -> bool
I'd guess that you're accidentally trying to call Handle::set (which is unsafe and takes three non-self arguments) rather than JsArray::set (which is safe and takes two non-self arguments).
If that's the case, you need to force a deref_mut to occur. (_mut because JsArray::set takes &mut self.)
I haven't run into this sort of naming collision before, so I can't be certain whether the auto-deref is smart enough, but something like this may work:
(&mut js_arr).set(0, JsNumber::new(scope, 1000));
Failing that, two other things to try are:
JsArray::set(&mut js_arr, 0, JsNumber::new(scope, 1000));
(If the former example fails because it's too much like C++-style method overloading. This is known as Fully Qualified Syntax and is normally used to disambiguate when an object implements two traits which provide methods of the same name.)
Call js_arr.deref_mut() directly to get a mutable reference to the underlying JsArray, then call set on that.

Struct declaration order

If I define structs at the module level, I can reference not-yet defined structs.
struct S {
ComesLater c;
}
struct ComesLater {}
But If I do the same inside an unittest or a function block, it doesn't work:
unittest {
struct S {
ComesLater c;
}
struct ComesLater {}
}
Error: undefined identifier 'ComesLater'
Why is that? How can I get order-independent declarations inside functions? Is there some kind of forward-declaration in d? I need this because I generate structs using mixin and ordering the declarations in the order of their inner-dependencies would be quite some effort, sometimes impossible, if there are circularly referencing structs. (using pointers.)
Declarations inside functions, unittests, or anywhere else that statements can actually be executed are indeed order-dependent because their values may depend on the code before them running. Think of a local variable:
int a;
writeln(a);
a = b;
int b = get_user_line();
If order wasn't important there, when would the two functions get called? Would the user be asked for a line before the writeln as the declarations are rewritten?
The current behavior of making b an undefined variable error keeps it simple and straightforward.
It works independent of order in other contexts because there is no executable code that it can depend on, so there's no behavior that can change if the compiler needs to internally think about it differently.
So:
How can I get order-independent declarations inside functions?
Change the context such that there is no executable code... put it all inside another struct!
void main() { // or unittest { }
struct Holder {
static struct S {
C c;
}
static struct C {}
}
}
Since execution happens around the holder and doesn't happen inside it, the order of declaration inside doesn't matter again. Since you can define almost anything inside a struct, you can use this for variables, functions, other structs, and so on. Basically all you have to do is wrap your existing code inside the struct Holder {} brackets.
By making everything static inside, you can just use it like a container and reference the stuff with Holder.S, etc., on the outside.

Declaring a map in a separate file and reading its contents

I'm trying to declare a map in a separate file, and then access it from my main function.
I want Rust's equivalent (or whatever comes closest) to this C++ map:
static const std::map<std::string, std::vector<std::string>> table = {
{ "a", { "foo" } },
{ "e", { "bar", "baz" } }
};
This is my attempt in Rust.
table.rs
use std::container::Map;
pub static table: &'static Map<~str, ~[~str]> = (~[
(~"a", ~[~"foo"]),
(~"e", ~[~"bar", ~"baz"])
]).move_iter().collect();
main.rs
mod table;
fn main() {
println(fmt!("%?", table::table));
}
The above gives two compiler errors in table.rs, saying "constant contains unimplemented expression type".
I also have the feeling that the map declaration is less than optimal for the purpose.
Finally, I'm using Rust 0.8.
As Chris Morgan noted, rust doesn't allow you to run user code in order to initialize global variables before main is entered, unlike C++. So you are mostly limited to primitive types that you can initialize with literal expressions. This is, afaik, part of the design and unlikely to change, even though the particular error message is probably not final.
Depending on your use case, you might want to change your code so you're manually passing your map as an argument to all the functions that will want to use it (ugh!), use task-local storage to initialize a tls slot with your map early on and then refer to it later in the same task (ugh?), or use unsafe code and a static mut variable to do much the same with your map wrapped in an Option maybe so it can start its life as None (ugh!).

Resources