Hi, I am new at Rust. I have read most of the docs, which are FANTASTIC (the whole ecosystem is fantastic), but I still have about 23,000 stupid questions.
The first one. I want to have a simple "Persistence" object that encapsulates a DB connection and methods to read & write data. This is trivial in any language with classes.
Regarding the question at hand, I'm not entirely sure what you mean by the term “impl-local value (property)”. Maybe elaborate a bit? Perhaps even even outline the equivalent code you're thinking of in a different programming language, if that helps explaining your point?
You’re probably misunderstanding structures. Even though Rust has some OOP things (encapsulation (not completely true), polymorphism), it’s not just another OO-language. Rust just “wraps” low- and mid-level features in some abstractions (zero-cost in most cases). What I am trying to tell is that Rust feels better if you treat it as an assembler with some OO-like capabilities than if you treat it as an OO-language with some low-level capabilities. It’s usually better to start with Rust’s FP features rather than try to translate your OO-language (Java, C++, Python, etcetera) code to Rust.
Answering to your question, a structure is not a class and impl blocks are used just to declare associated items (there are no properties). If you want to specify field’s value in time of “construction”, you need to explicitly write a “constructor” and “initializer”.
pub struct Persistence {
client: Client,
}
impl Persistence {
pub async fn new() -> Option<Self> { // i don’t know which type do you expect to be here
let client_uri = "mongodb://127.0.0.1:27017";
let options = ClientOptions::parse(&client_uri).await?;
let client = Client::with_options(options)?;
Some(Self { // i don’t know which type do you expect to be here
client,
})
}
}
Constructors and initializers were just what I needed.
I'm not trying to treat Rust like an OO language. I am a recovering object-oriented programmer. I just wanted a way to do this simple thing - associate a (hidden) DB instance with functions that use it - and now I know.
Follow-up. I noticed that invoking new requires use of static ( :: ) syntax:
let db = persistence::Persistence::new().await.unwrap();
That makes sense - this is a type, not instance, method - but how does the compiler know that? Because this 'new' syntax is simply a convention from how I read the docs, not a language feature.
Any function which doesn't take an instance as its first argument is invoked through the struct name. You can think of these as static methods from java/c#, like your Persistence::new() example. If a function takes a self, &self, or &mut self argument as it's first argument, then it can be invoked like a class method from those languages using dot syntax on an instance of the struct, e.g. my_foo.bar(). The self parameter becomes essentially the same as a 'this' hidden argument from those languages, and can be used within the method in the same manner.
Example of a struct with a constructor and a setter/getter for a 'property'.
with an implicit this parameter. But in Rust, you can pass self in several ways so you have to be explicit. Note that the self syntax is special and marks a function as being callable "like a method" ie. with the foo.bar() notation. But in Rust you can also call any associated function, method or not, using the fully-qualified syntax Foo::bar(foo).