I am building a restful api using rocket.rs. I am a Rust beginner and learning rocket.rs. I have two database tables: chair and table. I use Rust Diesel ORM. The chair is represented by the Chair struct and table by the Table struct. A chair can refer to a table having Some(table_id) or to no table if None.
I am trying to create a POST endpoint that uses a chair id as parameter and transforms it into a Chair struct. Also, I want to validate that this chair is associated to a table. If the provided chair id is not associated to a table, then an error is sent. I have an implementation working already, but I feel that this is not a clean solution.
#[derive(Queryable, Serialize)]
pub struct Chair {
pub id: i32,
pub color: String,
pub table_id: Option<i32>
}
#[derive(Queryable, Serialize)]
pub struct ChairWithTable {
pub id: i32,
pub color: String,
pub table: i32
}
impl TryFrom<Chair> for ChairWithTable {
type Error = ChairError;
fn try_from(chair: Chair) -> Result<Self, Self::Error> {
if chair.table_id.is_none() {
Err(ChairError::WithoutTable)
} else {
Ok(ChairWithTable {
id: chair.id,
color: chair.color,
table_id: chair.table_id.unwrap()
})
}
}
}
pub struct Table {
pub id: i32,
// ... whatever other fields. this is not important in the example
}
impl<'a> FromParam<'a> for ChairWithTable {
// implemention details omitted
}
impl<'a> FromParam<'a> for Chair {
// implemention details omitted
}
Here we pretend we can add pieces (could be anything else, not very relevant to the case anyhow) to a chair but only if the chair is bound to a table. Does not make a lot of sense, but I changed the table names so bear with me.
#[post("/chairs/<chair>/pieces", rank = 1)]
async fn pieces(chair: ChairWithTable, conn: DatabaseConnection) -> Result<Json<[/*irrelevent*/]>, Forbidden<Option<String>>> {
// implementation details omitted
}
So what I feel is wrong about my solution, is that there is a lot of duplication between Chair and ChairWithTable. Actually I copied all the fields and removed the Option wrapping the i32. I also had to impl TryFrom<Chair> for ChairWithTable. This is already verbose and it would get much more complicated if I had even just two Option fields on the same model. I could also omit the ChairWithTable struct and only keep Chair struct. But then when I get in my controller method, I have to check if table_id is Some or None every time and throw an error mid way in the controller code instead of using the param guards as validation.
Coming from web dev background in php and TypeScript, my brain would try to solve this with TypeScript's utility types. I would do something like take the Chair struct and make the field required with something that looks like this type ChairWithTabl = Pick<Chair, 'id'|'color'> & Required<Pick<Chair, 'table_id'>>. Another way of solving it would for me to go with a library like Zod that creates types on the fly with type inference.
Think that one solution in pure Rust would be to aim for struct composition like this answer for this question. But as far as I know, it is not possible to use composition on diesel models
How would you recommend to tackle this kind problem in the context of Rust, Rocket.rs and Diesel?
I'm trying to implement a simple interpreter in Rust for a made up programming language called rlox, following Bob Nystrom's book Crafting Interpreters.
I want errors to be able to occur in any child module, and for them to be "reported" in the main module (this is done in the book, with Java, by simply calling a static method on the containing class which prints the offending token and line). However, if an error occurs, it's not like I can just return early with Result::Err (which is, I assume, the idiomatic way to handle errors in Rust) because the interpreter should keep running - continually looking for errors.
Is there an (idiomatic) way for me to emulate the Java behaviour of calling a parent class' static method from a child class in Rust with modules? Should I abandon something like that entirely?
I thought about a strategy where I inject a reference to some ErrorReporter struct as a dependency into the Scanner and Token structs, but that seems unwieldy to me (I don't feel like an error reporter should be part of the struct's signature, am I wrong?):
struct Token {
error_reporter: Rc<ErrorReporter>, // Should I avoid this?
token_type: token::Type,
lexeme: String,
line: u32
}
This is the layout of my project if you need to visualise what I'm talking about with regards to module relationships. Happy to provide some source code if necessary.
rlox [package]
└───src
├───main.rs (uses scanner + token mods, should contain logic for handling errors)
├───lib.rs (just exports scanner and token mods)
├───scanner.rs (uses token mod, declares scanner struct and impl)
└───token.rs (declares token struct and impl)
Literal translation
Importantly, a Java static method has no access to any instance state. That means that it can be replicated in Rust by either a function or an associated function, neither of which have any state. The only difference is in how you call them:
fn example() {}
impl Something {
fn example() {}
}
fn main() {
example();
Something::example();
}
Looking at the source you are copying, it doesn't "just" report the error, it has code like this:
public class Lox {
static boolean hadError = false;
static void error(int line, String message) {
report(line, "", message);
}
private static void report(int line, String where, String message) {
System.err.println(
"[line " + line + "] Error" + where + ": " + message);
hadError = true;
}
}
I'm no JVM expert, but I'm pretty sure that using a static variable like that means that your code is no longer thread safe. You simply can't do that in safe Rust; you can't "accidentally" make memory-unsafe code.
The most literal translation of this that is safe would use associated functions and atomic variables:
use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT};
static HAD_ERROR: AtomicBool = ATOMIC_BOOL_INIT;
struct Lox;
impl Lox {
fn error(line: usize, message: &str) {
Lox::report(line, "", message);
}
fn report(line: usize, where_it_was: &str, message: &str) {
eprintln!("[line {}] Error{}: {}", line, where_it_was, message);
HAD_ERROR.store(true, Ordering::SeqCst);
}
}
You can also choose more rich data structures to store in your global state by using lazy_static and a Mutex or RwLock, if you need them.
Idiomatic translation
Although it might be convenient, I don't think such a design is good. Global state is simply terrible. I'd prefer to use dependency injection.
Define an error reporter structure that has the state and methods you need and pass references to the error reporter down to where it needs to be:
struct LoggingErrorSink {
had_error: bool,
}
impl LoggingErrorSink {
fn error(&mut self, line: usize, message: &str) {
self.report(line, "", message);
}
fn report(&mut self, line: usize, where_it_was: &str, message: &str) {
eprintln!("[line {} ] Error {}: {}", line, where_it_was, message);
self.had_error = true;
}
}
fn some_parsing_thing(errors: &mut LoggingErrorSink) {
errors.error(0, "It's broken");
}
In reality, I'd rather define a trait for things that allow reporting errors and implement it for a concrete type. Rust makes this nice because there's zero performance difference when using these generics.
trait ErrorSink {
fn error(&mut self, line: usize, message: &str) {
self.report(line, "", message);
}
fn report(&mut self, line: usize, where_it_was: &str, message: &str);
}
struct LoggingErrorSink {
had_error: bool,
}
impl LoggingErrorSink {
fn report(&mut self, line: usize, where_it_was: &str, message: &str) {
eprintln!("[line {} ] Error {}: {}", line, where_it_was, message);
self.had_error = true;
}
}
fn some_parsing_thing<L>(errors: &mut L)
where
L: ErrorSink,
{
errors.error(0, "It's broken");
}
There's lots of variants of implementing this, all depending on your tradeoffs.
You could choose to have the logger take &self instead of &mut, which would force this case to use something like a Cell to gain internal mutability of had_error.
You could use something like an Rc to avoid adding any extra lifetimes to the calling chain.
You could choose to store the logger as a struct member instead of a function parameter.
For your extra keyboard work, you get the benefit of being able to test your errors. Simply whip up a dummy implementation of the trait that saves information to internal variables and pass it in at test time.
Opinions, ahoy!
a strategy where I inject a reference to some ErrorReporter struct as a dependency into the Scanner
Yes, dependency injection is an amazing solution to a large number of coding issues.
and Token structs
I don't know why a token would need to report errors, but it would make sense for the tokenizer to do so.
but that seems unwieldy to me. I don't feel like an error reporter should be part of the struct's signature, am I wrong?
I'd say yes, you are wrong; you've stated this as an absolute truth, of which very few exist in programming.
Concretely, very few people care about what is inside your type, probably only to be the implementer. The person who constructs a value of your type might care a little because they need to pass in dependencies, but this is a good thing. They now know that this value can generate errors that they need to handle "out-of-band", as opposed to reading some documentation after their program doesn't work.
A few more people care about the actual signature of your type. This is a double-edged blade. In order to have maximal performance, Rust will force you to expose your generic types and lifetimes in your type signatures. Sometimes, this sucks, but either the performance gain is worth it, or you can hide it somehow and take the tiny hit. That's the benefit of a language that gives you choices.
See also
How to synchronize a static variable among threads running different instances of a class in Java?
Where are static methods and static variables stored in Java?
Static fields in a struct in Rust
How can you make a safe static singleton in Rust?
How do I create a global, mutable singleton?
How can I avoid a ripple effect from changing a concrete struct to generic?
I wanted to implement a proc_macro_attribute such that:
#[component]
struct Div {
#[prop]
color: String,
clicked: bool
}
Where the impl of both the attributes are:
#[proc_macro_attribute]
pub fn component(_attribute: TokenStream, input: TokenStream) -> TokenStream {
modify_struct_and_generate_props_struct(input)
}
// Just as a placeholder attribute for the component attribute to work on.
#[proc_macro_attribute]
pub fn prop(_attribute: TokenStream, input: TokenStream) -> TokenStream {
input
}
I could do it with a custom derive, but that is not what I want to achieve here because any struct annotated with #[component] would be modified to include additional fields. The #[prop] sub attribute is used to create a newer struct made up of only prop fields.
The proc_macro_attribute does not support other sub-attributes used within its context and I tried to implement a separate attribute to mitigate that. This causes the compiler to error out with:
error[E0658]: The attribute `prop` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642)
--> src/main.rs:33:1
|
33 | #[component]
| ^^^^^^^^^^^^
|
= help: add #![feature(custom_attribute)] to the crate attributes to enable
Adding this feature conflicts with #![feature(proc_macro)].
I have defined the problem on GitHub. You could look it up to see what I am trying to accomplish.
Is there no other possible way out?
I have also filed an issue with Rust if this was not the way it was meant to be.
The proc_macro_attribute does not support other sub-attributes...
Actually it does.
The only thing is that, the compiler does not want any compiler unknown attributes on the final expanded struct. I was expanding the struct within the #[component] macro but leaving as-is the #[prop] attribute. This caused the compiler to search for meaning for the said macro and errored out.
So, to make it work, be sure to remove the attribute from the field after it has been used. Any residue will cause the compiler to error.
#[proc_macro_attribute]
pub fn component(_attribute: TokenStream, input: TokenStream) -> TokenStream {
let item: syn::Item = syn::parse(input).unwrap();
let props_struct = generate_props_struct(&item);
let modified_struct = modify_struct_and_remove_props_attribute(&item);
quote! {
#props_struct
#modified_struct
}
}
Also the struct does not require any placeholder attribute defined separately. Every attribute on the fields comes within the context of the proc_macro_attribute placed on the struct.
I have updated the github repo with comments to see how it could be done.
I need to implement the fmt::Display method for an object coming from an external crate, so I created a wrapper for this object. I'd like to be able to use all the methods from the original object, without having to redefine all of them. I tried to implement Deref as advised on the awesome IRC channel #rust-beginners:
struct CustomMap(ObjectComingFromAnExternalCrate<char, char>);
impl std::ops::Deref for CustomMap {
type Target = ObjectComingFromAnExternalCrate<char, char>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn main() {
let cm = CustomMap::with_capacity(10);
println!("Hello, world!");
}
However, I'm getting this error :
error: no associated item named `with_capacity` found for type `CustomMap` in the current scope
--> <anon>:16:13
|
16 | let a = CustomMap::with_capacity(10);
| ^^^^^^^^^^^^^^^^^^^^^^^^
I assume it's because deref() doesn't work with associated functions.
How can I work around this? Reimplementing every associated function I use, just to be able to implement one method I need seems like overkill.
Newtypes are specifically designed to provide encapsulation, so they do not necessarily lend them well to just "adding new stuff".
That being said, a combination of:
Deref and DerefMut to get access to the methods
From and Into to easily convert from one to the other
OR making the inner type pub
should be able to tackle this.
The From/Into recommendation comes from the fact that most associated functions are generally constructors1.
impl From<ObjectComingFromAnExternalCrate<char, char>> for CustomMap { ... }
and then you can do:
let cm: CustomMap = ObjectComingFromAnExternalCrate<char, char>::with_capacity(10).into();
The other solution is to define CustomMap as:
struct CustomMap(pub ObjectComingFromAnExternalCrate<char, char>);
and then:
let cm = CustomMap(ObjectComingFromAnExternalCrate<char, char>::with_capacity(10));
If you do not wish to enforce any other invariant, and do not care about encapsulation, either should get you going.
1 Pointer types, such as Rc, use them heavily to avoid hiding methods of the Deref'ed to type.
Consider the following implementation:
pub struct BST {
root: Link,
}
type Link = Option<Box<Node>>;
struct Node {
left: Link,
elem: i32,
right: Link,
}
impl Link { /* misc */ }
impl BST { /* misc */ }
I keep getting the error:
cannot define inherent impl for a type outside of the crate where the type is defined; define and implement a trait or new type instead
I was able to find others had this same issue back in February, but there was seemingly no solution at the time.
Is there any fix or another way for me to implement my Link typedef in Rust?
Is there any fix
Not really. A type alias (type Foo = Bar) does not create a new type. All it does is create a different name that refers to the existing type.
In Rust, you are not allowed to implement inherent methods for a type that comes from another crate.
another way for me to implement
The normal solution is to create a brand new type. In fact, it goes by the name newtype!
struct Link(Option<Box<Node>>);
impl Link {
// methods all up in here
}
There's no runtime disadvantage to this - both versions will take the exact same amount of space. Additionally, you won't accidentally expose any methods you didn't mean to. For example, do you really want clients of your code to be able to call Option::take?
Another solution is to create your own trait, and then implement it for your type. From the callers point of view, it looks basically the same:
type Link = Option<Box<Node>>;
trait LinkMethods {
fn cool_method(&self);
}
impl LinkMethods for Link {
fn cool_method(&self) {
// ...
}
}
The annoyance here is that the trait LinkMethods has to be in scope to call these methods. You also cannot implement a trait you don't own for a type you don't own.
See also:
How do I implement a trait I don't own for a type I don't own?