I'm trying to store clap::ArgMatches in a struct like so:
struct Configurator {
cli_args: ArgMatches,
root_directory: String
}
The error I receive is:
error[E0106]: missing lifetime specifier
--> src/configurator.rs:5:15
|
5 | cli_args: ArgMatches,
| ^^^^^^^^^^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
4 | struct Configurator<'a> {
5 | cli_args: ArgMatches<'a>,
|
I tried the suggested solution that is given in the error output, but that appears to cause different errors.
Here's a little more context:
extern crate clap;
use clap::{Arg, App, ArgMatches};
struct Configurator {
cli_args: ArgMatches,
root_directory: String
}
impl Configurator {
pub fn build() -> Configurator {
let configurator = Configurator {};
// returns ArgMatches apparently
let cli_args = App::new("Rust Web Server")
.version("0.0.1")
.author("Blaine Lafreniere <brlafreniere#gmail.com>")
.about("A simple web server built in rust.")
.arg(Arg::with_name("root_dir")
.short("r")
.long("root_dir")
.value_name("ROOT_DIR")
.help("Set the root directory that the web server will serve from.")
.takes_value(true))
.get_matches();
configurator.cli_args = cli_args;
}
}
As the error message suggests, you have to add named lifetime specifiers to Configurator. This is because ArgMatches holds references to values, so you must tell Rust how long those references will live:
struct Configurator<'a> {
cli_args: ArgMatches<'a>,
root_directory: String
}
You must do the same for the impl block:
impl<'a> Configurator<'a> {
pub fn build() -> Configurator<'a> {
// ...
}
}
The other issue with your code is that in Rust, you must initialize all fields when instantiating a struct:
// this doesn't work
let config = Configurator {}
// but this does
let config = Configurator {
cli_args: cli_args,
root_directory: "/home".to_string(),
};
Finally, you have to return the Configurator from the function:
pub fn build() -> Configurator<'a> {
let cli_args = App::new("Rust Web Server")
.version("0.0.1")
.author("Blaine Lafreniere <brlafreniere#gmail.com>")
.about("A simple web server built in rust.")
.arg(
Arg::with_name("root_dir")
.short("r")
.long("root_dir")
.value_name("ROOT_DIR")
.help("Set the root directory that the web server will serve from.")
.takes_value(true),
)
.get_matches();
return Configurator {
cli_args: cli_args,
root_directory: "/home".to_string(),
};
}
Here is a runnable example.
Since you build the App using only 'static strings then the return type of .arg_matches() is ArgMatches<'static> which you should explicitly state in your Configurator struct definition. This is much better than using a generic lifetime parameter like 'a since it doesn't "infect" your Configurator struct with any lifetime annotations. Also, you cannot "partially" initialize structs in Rust, they must be fully initialized, so I've moved the construction of the Configurator struct to the bottom of the build function when all data is ready.
use clap::{Arg, App, ArgMatches};
struct Configurator {
cli_args: ArgMatches<'static>,
root_directory: String
}
impl Configurator {
pub fn build(root_directory: String) -> Configurator {
let cli_args = App::new("Rust Web Server")
.version("0.0.1")
.author("Blaine Lafreniere <brlafreniere#gmail.com>")
.about("A simple web server built in rust.")
.arg(Arg::with_name("root_dir")
.short("r")
.long("root_dir")
.value_name("ROOT_DIR")
.help("Set the root directory that the web server will serve from.")
.takes_value(true))
.get_matches();
Configurator {
cli_args,
root_directory,
}
}
}
playground
Related
I have this code to initialize the metrics registry
use once_cell::sync::Lazy;
pub static REQUEST_COUNTER: Lazy<IntCounter> = Lazy::new(|| {
register_int_counter!("requests_total", "Request counter").expect(REGISTRY_ERR)});
pub static ERROR_COUNTER: Lazy<IntCounter> = Lazy::new(|| {
register_int_counter!("errors_total", "Error counter").expect(REGISTRY_ERR)});
...
pub fn create_registry() -> Registry {
let registry = Registry::new();
registry.register(Box::new(REQUEST_COUNTER.clone())).expect(REGISTRY_ERR);
registry.register(Box::new(ERROR_COUNTER.clone())).expect(REGISTRY_ERR);
...
registry
}
I want to extract the repetitive part into a new function
pub fn create_registry() -> Registry {
...
register_collector(®istry, &REQUEST_COUNTER);
...
registry
}
fn register_collector <T> (registry: &Registry, collector: &Lazy<T>) where T: Collector + Clone {
registry.register(Box::new(collector.clone())).expect(REGISTRY_ERR);
}
but rust compiler shows an error for this function signature:
|
37 | registry.register(Box::new(collector.clone())).expect(REGISTRY_ERR);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Collector` is not implemented for `&once_cell::sync::Lazy<T>`
|
= note: required for the cast to the object type `dyn Collector`
What is the correct signature for "register_collector" function?
register_collector is passed a borrowed Lazy reference, so it needs to be dereferenced with *:
.register(Box::new((*collector).clone()))
When I tried to build an application with axum, I failed to separate the framework from my handler. With Go, the classic way is define an Interface, implement it and register the handler to framework. In this way, it's easy to provide a mock handler to test with. However, I couldn't make it work with Axum. I defined a trait just like above, but it wouldn't compile:
use std::net::ToSocketAddrs;
use std::sync::{Arc, Mutex};
use serde_derive::{Serialize, Deserialize};
use serde_json::json;
use axum::{Server, Router, Json};
use axum::extract::Extension;
use axum::routing::BoxRoute;
use axum::handler::get;
#[tokio::main]
async fn main() {
let app = new_router(
Foo{}
);
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
trait Handler {
fn get(&self, get: GetRequest) -> Result<GetResponse, String>;
}
struct Foo {}
impl Handler for Foo {
fn get(&self, req: GetRequest) -> Result<GetResponse, String> {
Ok(GetResponse{ message: "It works.".to_owned()})
}
}
fn new_router<T:Handler>(handler: T) -> Router<BoxRoute> {
Router::new()
.route("/", get(helper))
.boxed()
}
fn helper<T:Handler>(
Extension(mut handler): Extension<T>,
Json(req): Json<GetRequest>
) -> Json<GetResponse> {
Json(handler.get(req).unwrap())
}
#[derive(Debug, Serialize, Deserialize)]
struct GetRequest {
// omited
}
#[derive(Debug, Serialize, Deserialize)]
struct GetResponse {
message: String
// omited
}
error[E0599]: the method `boxed` exists for struct `Router<axum::routing::Layered<Trace<axum::routing::Layered<AddExtension<Nested<Router<BoxRoute>, Route<axum::handler::OnMethod<fn() -> impl Future {direct}, _, (), EmptyRouter>, EmptyRouter<_>>>, T>>, SharedClassifier<ServerErrorsAsFailures>>>>`, but its trait bounds were not satisfied
--> src/router.rs:25:10
|
25 | .boxed()
| ^^^^^ method cannot be called on `Router<axum::routing::Layered<Trace<axum::routing::Layered<AddExtension<Nested<Router<BoxRoute>, Route<axum::handler::OnMethod<fn() -> impl Future {direct}, _, (), EmptyRouter>, EmptyRouter<_>>>, T>>, SharedClassifier<ServerErrorsAsFailures>>>>` due to unsatisfied trait bounds
|
::: /Users/lebrancebw/.cargo/registry/src/github.com-1ecc6299db9ec823/axum-0.2.5/src/routing/mod.rs:876:1
|
876 | pub struct Layered<S> {
| --------------------- doesn't satisfy `<_ as tower_service::Service<Request<_>>>::Error = _`
|
= note: the following trait bounds were not satisfied:
`<axum::routing::Layered<Trace<axum::routing::Layered<AddExtension<Nested<Router<BoxRoute>, Route<axum::handler::OnMethod<fn() -> impl Future {direct}, _, (), EmptyRouter>, EmptyRouter<_>>>, T>>, SharedClassifier<ServerErrorsAsFailures>>> as tower_service::Service<Request<_>>>::Error = _`
I guess the key point is my design is apparently not "rustic". Is there a way to structure an Axum project that lends itself to testing easily?
The question is what you want to test. I will assume that you have some core logic and an HTTP layer. And you want to make sure that:
requests are routed correctly;
requests are parsed correctly;
the core logic is called with the expected parameters;
and return values from the core are correctly formatted into HTTP responses.
To test it you want to spawn an instance of the server with the core logic mocked out.
#lukemathwalker in his blog and book "Zero To Production In Rust" has a very nice description of how to spawn an app for testing through the actual TCP port. It is written for Actix-Web, but the idea applies to Axum as well.
You should not use axum::Server::bind, but rather use axum::Server::from_tcp to pass it a std::net::TcpListner which allows you to spawn a test server on any available port using `TcpListener::bind("127.0.0.1:0").
To make the core logic injectable (and mockable) I declare it as a struct and implement all the business methods on it. Like this:
pub struct Core {
public_url: Url,
secret_key: String,
storage: Storage,
client: SomeThirdPartyClient,
}
impl Core {
pub async fn create_something(
&self,
new_something: NewSomething,
) -> Result<Something, BusinessErr> {
...
}
With all these pieces you can write a function to start the server:
pub async fn run(listener: TcpListener, core: Core)
This function should encapsulate things like routing configuration, server logging configuration and so on.
Core can be provided to handlers using Extension Layer mechanism like this:
...
let shared_core = Arc::new(core);
...
let app = Router::new()
.route("/something", post(post_something))
...
.layer(AddExtensionLayer::new(shared_core));
Which in a handler can be declared in parameter list using extension extractor:
async fn post_something(
Extension(core): Extension<Arc<Core>>,
Json(new_something): Json<NewSomething>,
) -> impl IntoResponse {
core
.create_something(new_something)
.await
}
Axum examples contain one on error handling and dependency injection. You can check it here.
Last but not least, now you can mock Core out with a library like mockall, write spawn_app function that would return host and port where the server is run, run some requests against it and do assertions.
The video from Bogdan at Let's Get Rusty channel provides a good start with mockall.
I will be happy to provide more details if you feel something is missing from the answer.
I am using tikv/raft-rs library for implementing a consensus system. This library has a RawNode object which is a thread-unsafe object. We must execute some functions on this object periodically (example), hence I use a new thread for executing.
Here are the constraints:
I need to have this object on the main-thread doesn't have this object for accessing some its internal states. (e.g.: raw_node.is_leader)
This object must be accessed on a worker thread.
Because of these constraints, this seems impossible because:
If I create this object, and move to the new thread, the main thread cannot call its state.
If I keep this object on the main thread object, I cannot use Arc<Mutex<>> because this object doesn't implement Copy method.
Here is my code:
use std::thread::JoinHandle;
use std::sync::{Arc, Mutex, mpsc};
use std::collections::{VecDeque, HashMap};
use raft::RawNode;
use std::sync::mpsc::{Receiver, Sender, TryRecvError};
use protobuf::Message as PbMessage;
use raft::eraftpb::ConfState;
use raft::storage::MemStorage;
use raft::{prelude::*, StateRole};
use regex::Regex;
use crate::proposal::Proposal;
use crate::{proposal, batch};
use std::{str, thread};
use std::time::{Instant, Duration};
use crate::batch::Mailbox;
pub struct Node {
core: Arc<Mutex<CoreNode>>
}
#[derive(Copy)]
struct CoreNode {
raft_group: RawNode<MemStorage>,
}
impl Node {
pub fn new() -> Self {
let cfg = Config {
election_tick: 10,
heartbeat_tick: 3,
..Default::default()
};
let storage = MemStorage::new();
let core = Arc::new(Mutex::new(CoreNode {
raft_group: RawNode::new(&cfg, storage).unwrap()
}));
thread::spawn(move || {
core.lock().unwrap().run();
return;
});
Node { core: core.clone() }
}
pub fn is_leader(&self) -> bool {
return self.core.lock().unwrap().raft_group.raft.state == StateRole::Leader;
}
}
impl CoreNode {
pub fn run(mut self) {}
}
When compiling, here is the error:
22 | #[derive(Copy)]
| ^^^^
23 | struct CoreNode {
24 | raft_group: RawNode<MemStorage>,
| ------------------------------- this field does not implement `Copy`
|
My question is: How can I design around this problem.
I'm using actix-web to create a httpserver with state/data embedded in it. But vscode show me that the create_app function has wrong arguments in its return value type definition App<AppState>:
pub struct App<T, B>
wrong number of type arguments: expected 2, found 1
expected 2 type argumentsrustc(E0107)
app.rs:
use crate::api;
use crate::model::DbExecutor;
use actix::prelude::Addr;
use actix_web::{error, http::Method, middleware::Logger, web, App, HttpResponse};
pub struct AppState {
pub db: Addr<DbExecutor>,
}
pub fn create_app(db: Addr<DbExecutor>) -> App<AppState> {
App::new().data(AppState { db }).service(
web::resource("/notes/").route(web::get().to(api::notes))
);
}
main.rs:
fn main() {
HttpServer::new(move || app::create_app(addr.clone()))
.bind("127.0.0.1:3000")
.expect("Can not bind to '127.0.0.1:3000'")
.start();
}
As return type of "service" method is "Self" which is type actix_web::App, I tried modify return type to App (without generic parameter) but still got error, what should I do?
First, App takes two generic type arguments, App<AppEntry, Body>, you've only given one.
Second, AppState is not AppEntry.
Third, instantiating App outside actix-web is hard, if not impossible, as the types you need from actix-web are not public.
Instead, you should use configure to achieve the same, here is a simplified example:
use actix_web::web::{Data, ServiceConfig};
use actix_web::{web, App, HttpResponse, HttpServer};
fn main() {
let db = String::from("simplified example");
HttpServer::new(move || App::new().configure(config_app(db.clone())))
.bind("127.0.0.1:3000")
.expect("Can not bind to '127.0.0.1:3000'")
.run()
.unwrap();
}
fn config_app(db: String) -> Box<Fn(&mut ServiceConfig)> {
Box::new(move |cfg: &mut ServiceConfig| {
cfg.data(db.clone())
.service(web::resource("/notes").route(web::get().to(notes)));
})
}
fn notes(db: Data<String>) -> HttpResponse {
HttpResponse::Ok().body(["notes from ", &db].concat())
}
Read more about ServiceConfig in the api documentation.
In Java-speak, I am trying to create a collection (vector) of objects (strict instances), each one of which implements an interface (trait), so I can then iterate over the collection and call a method on all of them.
I have reduced it down to one sample file below which contains all the parts that I hope will make it easier to get answers.
// main.rs - try and compile using just "rustc main.rs"
use std::io::Result;
/// ////// Part 1
// Types used by implementors of the trait, and in creating a vector of implementors of the trai
pub struct SampleResult {
metric: String,
}
pub trait SampleRunner {
fn run(&self, &'static str) -> Result<SampleResult>;
}
pub struct Sample {
name: &'static str,
runner: &'static SampleRunner,
}
/// /////// Part 2
/// Create one specific static instance of such as Sample
static SAMPLE1: Sample = Sample {
name: "sample",
runner: &Sample1,
};
// need a struct to hold the run method to satisfy the trait???
struct Sample1;
// Implement the trait for this specific struct
impl SampleRunner for Sample1 {
fn run(&self, name: &'static str) -> Result<SampleResult> {
println!("Name: {}", name);
Ok(SampleResult { metric: "OK".to_string() })
}
}
/// /////// Part 3
/// Using the existing static instances of Sample struct, by creating a vector of references for them
/// then iterating over the vector and calling the trait method on each one
fn main() {
let sample_set: Vec<&Sample> = vec![&SAMPLE1];
for sample in sample_set.iter() {
match sample.runner.run(sample.name) {
Ok(result) => println!("Success"),
_ => panic!("failed"),
}
}
}
That particular example fails with the message:
error[E0277]: the trait bound `SampleRunner + 'static: std::marker::Sync` is not satisfied in `Sample`
--> <anon>:21:1
|
21 | static SAMPLE1: Sample = Sample {
| _^ starting here...
22 | | name: "sample",
23 | | runner: &Sample1,
24 | | };
| |__^ ...ending here: within `Sample`, the trait `std::marker::Sync` is not implemented for `SampleRunner + 'static`
|
= note: `SampleRunner + 'static` cannot be shared between threads safely
= note: required because it appears within the type `&'static SampleRunner + 'static`
= note: required because it appears within the type `Sample`
= note: shared static variables must have a type that implements `Sync`
But I have had many different problems depending on the approach I have taken, related to Sync, Sized, etc etc.
There are two little errors in the code. The first is described by the error, it tells us that the static value is not safe as it does not implement the Sync trait. It just tries to prepare for the case when the static value is manipulated from multiple threads. Here, the best solution is simply to mark the value as const. After that, there is some problem with the lifetime of &SAMPLE1 in main, can be solved by "using the let keyword to increase it's lifetime".
The code after these little modifications compiles, and looks like this:
use std::io::{Result};
pub struct SampleResult {
metric: String
}
pub trait SampleRunner {
fn run(&self, &'static str) -> Result<SampleResult>;
}
pub struct Sample {
name: &'static str,
runner: &'static SampleRunner
}
// Make it const
const SAMPLE1: Sample = Sample { name: "sample", runner: &Sample1 };
struct Sample1;
impl SampleRunner for Sample1 {
fn run(&self, name: &'static str) -> Result<SampleResult> {
println!("Name: {}", name);
Ok(SampleResult {metric: "OK".to_string() })
}
}
fn main() {
// Extend the lifetime of the borrow by assigning it to a scope variable
let borrowed_sample1 : &Sample = &SAMPLE1;
let sample_set: Vec<&Sample> = vec!(borrowed_sample1);
for sample in sample_set.iter() {
match sample.runner.run(sample.name) {
Ok(result) => println!("Success"),
_ => panic!("failed")
}
}
}
However, I see that you are not satisfied with the code as you have to create a new struct for every implementation of SampleRunner. There is another way, using lambda functions (the Rust docs just refers to them as Closures).
use std::io::{Result};
pub struct SampleResult {
metric: String
}
type SampleRunner = Fn(&'static str) -> Result<SampleResult>;
pub struct Sample {
name: &'static str,
runner: &'static SampleRunner
}
// Still const, use a lambda as runner
const SAMPLE1: Sample = Sample { name: "sample", runner: &|name| {
println!("Name: {}", name);
Ok(SampleResult {metric: "OK".to_string() })
} };
fn main() {
let borrowed_sample1 : &Sample = &SAMPLE1;
let sample_set: Vec<&Sample> = vec!(borrowed_sample1);
for sample in sample_set.iter() {
// Must parenthese sample.runner so rust knows its a field not a method
match (sample.runner)(sample.name) {
Ok(result) => println!("Success"),
_ => panic!("failed")
}
}
}