Struct impl properties

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.

I've tried several things, including this:

pub struct Persistence {
    client: Client
}

impl Persistence {
    self.client = {
        let client_uri = "mongodb://127.0.0.1:27017";
        let options = ClientOptions::parse(&client_uri).await?;
        Client::with_options(options)?;
    }
}

Doesn't compile.

I can easily declare a private field in a struct and use it in a method in an impl, but that's not what I need.

How can I simply have an impl-local value (property)? What's the idiomatic way to do this?

Hi and welcome! Make sure to check out the pinned post on code blocks / syntax highlighting: Forum Code Formatting and Syntax Highlighting

You can edit your post using the pencil symbol :wink:

1 Like

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?

I mean a struct instance that owns a value - just as a class instance in any language with classes can have properties.

For ex, Java:

class Persistence {
  private Database db = ...
  ...
}

I want an object (struct instance) to encapsulate a DB connection and methods that use it...

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,
        })
    }
}
2 Likes

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'.

1 Like

Within an impl,

pub fn foo() -> Bar

is the equivalent to Java's

public static Bar foo()

whereas

pub fn foo(&mut self) -> Bar

is equivalent to

public Bar foo()

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).

1 Like

Doh!! I actually knew that one. :slight_smile:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.