Would like to make this function generic but having trouble specifying the bounds for parameter T
Errors : 1)'cannot find type DB in this scope
not found in this scope'
Not sure if I was supposed to include ::bind either. I got that from rust-analyzer
pub async fn set_db_value<
'q,
T: std::marker::Sync
+ std::marker::Send
+ sqlx::query::Query<'q, DB, <DB as HasArguments<'q>>::Arguments>::bind,
>(
key: &str,
b: T,
) {
let statement = format!("update preferences set {} = $1 where key = 'p'", &key);
sqlx::query(&statement)
.bind(&b)
.execute(&pg)
.await
.unwrap();
}
Related
I have a series of traits that are mutually associated, and contain further type associations that are meant to be enums. I've left the Id type associations repeated in each trait that needs it, to avoid deeply nested fully-qualified names like <<World as World>::Exit as Exit>::SpotId but this is leading to confusion now that I'm implementing behavior relying on these "top-level" traits:
trait Accessible {
type Context: Ctx;
fn can_access(&self, ctx: &Self::Context) -> bool;
}
trait Id: Copy + Clone + Debug + Eq + Hash + Ord + PartialOrd {}
trait Location: Accessible {
type LocId: Id;
type ExitId: Id;
}
trait Exit: Accessible {
type ExitId: Id;
type SpotId: Id;
type LocId: Id;
}
trait World {
type Context: Ctx;
type Location: Location;
type Exit: Exit;
type SpotId: Id;
fn get_neighbors(&self, spot_id: Self::SpotId) -> &[Self::SpotId];
}
trait Ctx: Clone + Eq {
type World: World<Context = Self, SpotId = Self::SpotId>;
type SpotId: Id;
}
fn expand<T>(world: &impl World<Context = T>, ctx: &T, start: <T as Ctx>::SpotId)
where T: Ctx,
{
for spot in world.get_neighbors(start) { }
}
(on the rust playground)
This produces an error on world.get_neighbors:
| expected `World::SpotId`, found `Ctx::SpotId`
= note: expected associated type `<impl World<Context = T> as World>::SpotId`
found associated type `<T as context::Ctx>::SpotId`
and vice versa on some other uses of a SpotId.
Is there a clean way to maintain the mutual associations and have a simple typename for (e.g.) SpotId so that it doesn't matter which trait's version of SpotId is named?
I've tried to add more annotations to make clear that they must be inferred to be the same type:
trait World {
type Context: Ctx<SpotId = Self::SpotId, World = Self>;
type Location: Location + Accessible<Context = Self::Context>;
type Exit: Exit<SpotId = Self::SpotId> + Accessible<Context = Self::Context>;
type SpotId: Id;
}
but get the same error.
I've looked at https://users.rust-lang.org/t/type-alias-for-readability-with-associated-traits/64044/2 which recommends using type aliases to manage fully-qualified names, but I've been unsure how to get started with that.
Update: it's possible to specify that one type fulfills both requirements via a type parameter:
fn expand<T, S>(world: &impl World<Context = T, SpotId = S>, ctx: &T, start: S)
where T: Ctx<SpotId = S>, S: Id,
{
for spot in world.get_neighbors(start) { }
}
but as development continues, my functions start looking more like
fn explore<T, S, L, E>(
world: &impl World<
Context = T,
SpotId = S,
Exit = impl Exit<ExitId = E, SpotId = S> + Accessible<Context = T>,
Location = impl Location<LocId = L> + Accessible<Context = T>,
>,
ctx: &T,
)
where
T: Ctx<SpotId = S, LocationId = L, ExitId = E> + Debug,
S: Id,
L: Id,
E: Id,
{ ... }
and every caller has to be modified whenever I add a use of another id type (there are a few more I omitted here for brevity). I should probably seek a solution at the trait level.
All you have to do is require that these associated types are the same type in the definition of fn expand, which you can do by introducing another generic parameter:
fn expand<T: Ctx<SpotId = S>, S>(
world: &impl World<Context = T, SpotId = S>,
ctx: &T,
start: S
) {
for spot in world.get_neighbors(start) { }
}
Adding the parameter S and constraining both Ctx and World to have SpotId = S tells the compiler that they must be the same type in order to allow this function to be invoked.
(Playground)
Here's what ended up working. Ignoring the circular dependency in Accessible, I reorganized types so that World only has one path to each Id type, and then used fully-qualified syntax in the trait functions. Then, I changed the generic types in the functions to use the types that implement the place traits rather than the Ids, since it's easier to write the trait bounds, including some odd cases like this struct I added that really only needs Ctx and SpotId but has to provide the bound that they're related.
trait World {
type Location: Location;
type Exit: Exit<
ExitId = <Self::Location as Location>::ExitId,
LocId = <Self::Location as Location>::LocId,
Context = <Self::Location as Accessible>::Context,
>;
fn get_neighbors(&self, spot_id: <Self::Exit as Exit>::SpotId) -> &[<Self::Exit as Exit>::SpotId];
}
trait Ctx {
type World: World;
}
struct SpotAccess<T, S>
where
T: Ctx,
S: Id,
<T::World as World>::Exit: Exit<SpotId = S>,
{ ... }
fn expand<W, T, E>(world: &W, ctx: &T, start: SpotAccess<T, E::SpotId>)
where
W: World<Exit = E>,
T: Ctx<World = W>,
E: Exit<Context = T>,
{ ... }
I'm not sure if the bound on World::Exit that its associated types are the same as World::Location's but is required but I'll leave it for now.
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>>;
// ...
}
I've got this function that is doing some work with manually created futures.
pub type Response = Pin<Box<dyn Future<Output = Result<T, Error>> + Send>>
pub fn get_list<T: DeserializeOwned + Send + 'static, P: serde::Serialize + Clone + Send>(
&self,
path: &str,
params: P,
) -> Response<List<T>> {
use futures::future::FutureExt;
let resp: Response<List<T>> = self.get_query(path, params.clone());
let params = params.clone();
let resp = resp.then(move |res| async move {
let params = params; // Take ownership of params.
match res {
Ok(list) => list.params(¶ms),
Err(e) => Err(e),
}
});
return Box::pin(resp);
}
I'm getting the error:
the parameter type `P` may not live long enough
...so that the type `futures_util::future::Then<Pin<Box<dyn futures_util::Future<Output = Result<List<T>, error::Error>> + std::marker::Send>>, impl futures_util::Future, [closure#src/client/async.rs:142:30: 148:10]>` will meet its required lifetime bounds
I don't want to make P static, but I don't mind cloning my way to a solution. My understanding is that using async move should move params and take ownership, so I don't need any sort of lifetime on P. But obviously there's still something weird going on with lifetimes.
Any suggestions?
For this to work, you need P: 'static. Right now, you are cloning params, but that doesn't prevent the type P from containing references that would become invalid at some point, regardless of whether those references have been cloned. By adding the P: 'static bound, you prohibit P from containing such references.
P: 'static can be read as "values of type P may be kept around as long as their owner wants". It does not mean that params will be required to live indefinitely, only that it could.
This question already has answers here:
Is there another option to share an Arc in multiple closures besides cloning it before each closure?
(2 answers)
Closed 2 years ago.
I am trying to implement a filter that sits in all my routes and extracts a header and matches a possible token to what is stored on my system.
I want to implement something like the warp rejection example but I get the error
expected a closure that implements the Fn trait, but this closure
only implements FnOnce closure is FnOnce because it moves the
variable tmp out of its environment
I kind of get what the compiler saying, but don't know how to solve it.
I thought doing let tmp = store.clone() would.
I have the filter:
pub fn haystack_auth_header(store: Store) -> impl Filter<Extract = (Store,), Error = Rejection> + Clone {
let tmp = store.clone();
warp::header("Authorization").and_then (|auth_header: String| async move {
// Authorization: BEARER authToken=xxxyyyzzz
let result = auth_token(&auth_header); //-> IResult<&'a str, (&'a str, &'a str), (&'a str, ErrorKind)> {
if result.is_err() {
return Err(reject::custom(HayStackAuthToken));
}
let (_, key_value) = result.unwrap();
let auth_token_result = tmp.read().get_authtoken();
if auth_token_result.is_err() {
return Err(reject::custom(HayStackAuthToken));
}
let auth_token_option = auth_token_result.unwrap();
if auth_token_option.is_none() {
return Err(reject::custom(HayStackAuthToken));
}
let auth_token = auth_token_option.unwrap();
if auth_token != key_value.1 {
return Err(reject::custom(HayStackAuthToken));
}
Ok(tmp)
})
}
store is type Store = Arc<RwLock<Box<dyn UserAuthStore>>> and UserAuthStore is trait UserAuthStore: fmt::Debug + Send + Sync.
UserAuthStore is defined as
pub trait UserAuthStore: fmt::Debug + Send + Sync {
// Return handshake token for username. If user has no handshake token generate one
fn get_handshake_token(&self, username: &str) -> HaystackResult<String>;
fn get_username(&self, handshake_token: &str) -> HaystackResult<String>;
fn set_temporary_value(&mut self, k: &str, v: &str) -> HaystackResult<()>;
fn get_temporary_value(&self, k: &str) -> HaystackResult<Option<&String>>;
fn set_authtoken(&mut self, s: String) -> HaystackResult<()>;
/// returns a base64 encoded sha256 salt of password.
fn get_password_salt(&self) -> HaystackResult<String>;
fn get_salted_password(&self) -> HaystackResult<String>;
fn get_authtoken(&self) -> HaystackResult<Option<String>>;
}
Why does clone not work here?
The full error is
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
--> src/server/mod.rs:997:45
|
997 | warp::header("Authorization").and_then (|auth_header: String| async move {
| ___________________________________--------__^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^_-
| | | |
| | | this closure implements `FnOnce`, not `Fn`
| | the requirement to implement `Fn` derives from here
998 | |
999 | | // Authorization: BEARER authToken=xxxyyyzzz
1000 | | let result = auth_token(&auth_header); //-> IResult<&'a str, (&'a str, &'a str), (&'a str, ErrorKind)> {
... |
1026 | | Ok(tmp.clone())
1027 | | })
| |_____- closure is `FnOnce` because it moves the variable `tmp` out of its environment
You can see a simplified test case here
After creating a simple test case I managed to get it going with the following function.
pub fn haystack_auth_header(store: Store) -> impl Filter<Extract = (Store,), Error = Rejection> + Clone {
warp::header("Authorization").and_then (
move |auth_header: String|
{
let tmp = store.clone();
async move {
let tmp = tmp.clone();
if tmp.read().get_authtoken().is_none() {
return Err(reject::custom(HayStackAuthToken));
}
Ok(tmp.clone())
}
}
)
}
So in the end just needed clone in the correct place.
This question suggests that you don't have yet a clear distinction about the fn hierarchy:
taken from here.
Warp's filter function need to implement the Fn trait.
This means that you cannot access the outer context(read: environment).
The first time AndThen call your closure, the function will consume the "only" tmp variable available, thus it won't be available for future calls. Since the closure it's taking ownership of its context, it means it's implementing FnOnce.
As suggested by #msrd0 in the comment, probably this is solvable by moving that call in the function, but I would need an MRE to be certain about it.
I created a structure where an iterator over a file or stdin should be stored, but compiler yelling at me :)
I decided that Lines is the struct I need to store in my struct to iterate using it later and Box will allow to store variable with unknown size, so I define my structure like that:
pub struct A {
pub input: Box<Lines<BufRead>>,
}
I want to do something like this later:
let mut a = A {
input: /* don't know what should be here yet */,
};
if something {
a.input = Box::new(io::stdin().lock().lines());
} else {
a.input = Box::new(BufReader::new(file).lines());
}
And finally
for line in a.input {
// ...
}
But I got an error from the compiler
error[E0277]: the size for values of type `(dyn std::io::BufRead + 'static)` cannot be known at compilation time
--> src/context.rs:11:5
|
11 | pub input: Box<Lines<BufRead>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `(dyn std::io::BufRead + 'static)`
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-sized>
= note: required by `std::io::Lines`
How can I achieve my goal?
The most generic answer to your question is that you don't / can't. Locking stdin returns a type that references the Stdin value. You cannot create a local value (stdin()), take a reference to it (.lock()), and then return that reference.
If you just want to do this inside of a function without returning it, then you can create a trait object:
use std::io::{self, prelude::*, BufReader};
fn example(file: Option<std::fs::File>) {
let stdin;
let mut stdin_lines;
let mut file_lines;
let input: &mut Iterator<Item = _> = match file {
None => {
stdin = io::stdin();
stdin_lines = stdin.lock().lines();
&mut stdin_lines
}
Some(file) => {
file_lines = BufReader::new(file).lines();
&mut file_lines
}
};
for line in input {
// ...
}
}
Or create a new generic function that you can pass either type of concrete iterator to:
use std::io::{self, prelude::*, BufReader};
fn example(file: Option<std::fs::File>) {
match file {
None => finally(io::stdin().lock().lines()),
Some(file) => finally(BufReader::new(file).lines()),
}
}
fn finally(input: impl Iterator<Item = io::Result<String>>) {
for line in input {
// ...
}
}
You could put either the trait object or the generic type into a structure even though you can't return it:
struct A<'a> {
input: &mut Iterator<Item = io::Result<String>>,
}
struct A<I>
where
I: Iterator<Item = io::Result<String>>,
{
input: I,
}
If you are feeling adventurous, you might be able to use some unsafe code / crates wrapping unsafe code to store the Stdin value and the iterator referencing it together, which is not universally safe.
See also:
Is there a way to use locked standard input and output in a constructor to live as long as the struct you're constructing?
Is there any way to return a reference to a variable created in a function?
Why can't I store a value and a reference to that value in the same struct?
How can I store a Chars iterator in the same struct as the String it is iterating on?
Are polymorphic variables allowed?
input: Box<Lines<BufRead>>,
This is invalid because Lines is not a trait. You want either:
use std::io::{prelude::*, Lines};
pub struct A {
pub input: Lines<Box<BufRead>>,
}
Or
use std::io;
pub struct A {
pub input: Box<Iterator<Item = io::Result<String>>>,
}