I am currently implementing my first library in Rust, and as it is the case with many developers coming from other languages like Java etc. I am struggling to find right solutions for common patterns/use cases in Rust.
My Rust library has multiple structs. Many of them need to have access to some common configuration options which are essential for their function. For example:
pub struct A {
pub context: Context
}
pub struct B {
pub context: Context
}
pub struct Context {
pub dbUri: String
}
The context will be passed to each struct once when the struct is initialized. The context attribute values might change at runtime which should make these changes available to any dependent struct. Structs should be able to make changes to context if required. It might be required to make the context available to different threads in the future.
The best solution I could find so far is to use Mutex wrapped in Arc as described here.
I would like to know whether this is the right way to implement my requirement, and what are other available options?
Related
I'm trying to load JSON files that refer to structs implementing a trait. When the JSON files are loaded, the struct is grabbed from a hashmap. The problem is, I'll probably have to have a lot of structs put into that hashmap all over my code. I would like to have that done automatically. To me this seems to be doable with procedural macros, something like:
#[my_proc_macro(type=ImplementedType)]
struct MyStruct {}
impl ImplementedType for MyStruct {}
fn load_implementors() {
let implementors = HashMap::new();
load_implementors!(implementors, ImplementedType);
}
Is there a way to do this?
No
There is a core issue that makes it difficult to skip manually inserting into a structure. Consider this simplified example, where we simply want to print values that are provided separately in the code-base:
my_register!(alice);
my_register!(bob);
fn main() {
my_print(); // prints "alice" and "bob"
}
In typical Rust, there is no mechanism to link the my_print() call to the multiple invocations of my_register. There is no support for declaration merging, run-time/compile-time reflection, or run-before-main execution that you might find in other languages that might make this possible (unless of course there's something I'm missing).
But Also Yes
There are third party crates built around link-time or run-time tricks that can make this possible:
ctor allows you to define functions that are executed before main(). With it, you can have my_register!() create invididual functions for alice and bob that when executed will add themselves to some global structure which can then be accessed by my_print().
linkme allows you to define a slice that is made from elements defined separately, which are combined at compile time. The my_register!() simply needs to use this crate's attributes to add an element to the slice, which my_print() can easily access.
I understand skepticism of these methods since the declarative approach is often clearer to me, but sometimes they are necessary or the ergonomic benefits outweigh the "magic".
So I've noticed there are multiple ways to convert from &str to String, but (for the sake of my question) I'll only talk about two ways, which would be "foo".to_string() and "foo".into().
Now I know that those are fundamentally different. The first of those is a method, while the second one is a trait-(implementation).
I currently need a piece of code that converts a ObjectNode into a Node.
First I implemented this using the Into trait:
struct ObjectNode {}
impl Into<Node> for ObjectNode {
fn into(self) -> Node {
Node::Object(self)
}
}
enum Node {
Object(ObjectNode)
}
But then I noticed that I had used "foo".to_string() instead of "foo".into() in my code. Simple because that's the thing I read online in pieces of code by other people. So I thought about implementing:
impl ObjectNode {
fn into_node(self) -> Node {
Node::Object(self)
}
}
I'm now wondering if this "explicit" into_node method is somehow preferred over the "implicit" into, as into could be implemented for quiet a large number of types (which could lead to some... unexpected behavior, maybe?). Or maybe it's better to do it the other way around?
Well I guess you should implement From<ObjectNode> for Node rather than implementing Into<Node> for ObjectNode because the standard library for Into highlights it:-
One should avoid implementing Into and implement
From instead. Implementing From automatically
provides one with an implementation of Into thanks to
the blanket implementation in the standard library.
You should probably go for an implementation of From rather than creating a separate method for converting it. Most crates in the Rust ecosystem heavily depend on these functions for their type conversions.
Traits are used to group some functions to be implemented from a struct, but is it possible to access struct fields from within the trait?
I could imagine declaring fields inside the trait so that the fields are abstracted as well. I haven't found such a syntax; is there any other solution? Otherwise, it wouldn't be possible to have non-static methods using a trait, would it?
I know object oriented programming from C# and I'm playing around with Rust, trying to adapt the OOP functionality I already know from C#.
This sounds like you're misunderstanding how traits work. Traits can't have fields. If you want to provide access to a field from a trait, you need to define a method in that trait (like, say, get_blah).
If you're asking whether you can access fields of a struct from within that struct's implementation of a trait, then yes. The struct knows it's own type, so there's no problem.
trait Pet {
fn is_smelly(&self) -> bool;
}
struct Dog {
washed_recently: bool,
}
impl Pet for Dog {
fn is_smelly(&self) -> bool {
!self.washed_recently
}
}
If you're writing a default implementation of a trait (i.e. defining a method body within the trait), then no, you can't access fields. A default implementation can only use methods that are defined on the trait or in a super trait.
It would be useful to define fields in a trait's default implementation, so a struct implementing the trait would always have the same fields.
Apparently, the Rust team thinks the same but it is still a work in progress according to this RFC. It's a big change and it has been postponed, so I think the short answer is: you can't do it yet, but you might be able to do it in the future.
For now, you'll have to make do with less powerful traits.
You can make accessor function in default trait implementation, that must return field value/ref in child implementations, returning default value. Use it in other fn's in default implementation, and redefine accessor's in child implementation. Default implementation fn's will use redefined accessors as it's virtual fn's.
I'd like to implement my own version of a Rust standard library feature and use my version and the real standard library version interchangeably.
If the standard library feature were a trait, this would be easy. However, the standard library feature (in this case, std::sync::Condvar) is implemented as
pub struct Condvar {...}
impl Condvar {...}
I tried doing
impl Condvar for MyCondvar {...}
but got an error ("error[E0404]: expected trait, found struct Condvar")
How should I do this? I've also tried
pub trait CondvarTrait { // copy entire interface of Condvar }
impl CondvarTrait for Condvar {
// copy entire interface of Condvar again,
// implementing foo(&self, ...) by calling self.foo(...)
}
impl CondvarTrait for MyCondvar { // my own implementation }
which compiles, but is super verbose. Is there a better way?
You need to copy the entire interface of the type and reimplement it by calling the inherent methods.
See also:
How to call a method when a trait and struct use the same method name?
Is there a better way?
Not with how you've described the requirements.
That being said, you aren't introducing any abstraction suited to your domain. In many cases, you don't want the full power of the underlying type(s), but want to do some higher-level abstraction. If you create that abstraction as a trait, then you can implement the new trait for any useful types.
Traits are used to group some functions to be implemented from a struct, but is it possible to access struct fields from within the trait?
I could imagine declaring fields inside the trait so that the fields are abstracted as well. I haven't found such a syntax; is there any other solution? Otherwise, it wouldn't be possible to have non-static methods using a trait, would it?
I know object oriented programming from C# and I'm playing around with Rust, trying to adapt the OOP functionality I already know from C#.
This sounds like you're misunderstanding how traits work. Traits can't have fields. If you want to provide access to a field from a trait, you need to define a method in that trait (like, say, get_blah).
If you're asking whether you can access fields of a struct from within that struct's implementation of a trait, then yes. The struct knows it's own type, so there's no problem.
trait Pet {
fn is_smelly(&self) -> bool;
}
struct Dog {
washed_recently: bool,
}
impl Pet for Dog {
fn is_smelly(&self) -> bool {
!self.washed_recently
}
}
If you're writing a default implementation of a trait (i.e. defining a method body within the trait), then no, you can't access fields. A default implementation can only use methods that are defined on the trait or in a super trait.
It would be useful to define fields in a trait's default implementation, so a struct implementing the trait would always have the same fields.
Apparently, the Rust team thinks the same but it is still a work in progress according to this RFC. It's a big change and it has been postponed, so I think the short answer is: you can't do it yet, but you might be able to do it in the future.
For now, you'll have to make do with less powerful traits.
You can make accessor function in default trait implementation, that must return field value/ref in child implementations, returning default value. Use it in other fn's in default implementation, and redefine accessor's in child implementation. Default implementation fn's will use redefined accessors as it's virtual fn's.