Coming from a bash scripting background, Rust’s syntax is mind boggling. The code from HttpServer...
to await?
is a single object. Trying to figure out how it works is going to be my first task.
I will return later with results in the replies.
// Create Http server with websocket support
let settings_bind = settings.clone();
HttpServer::new(move || {
let context = LemmyContext::create(
pool.clone(),
chat_server.to_owned(),
client.clone(),
activity_queue.to_owned(),
settings.to_owned(),
secret.to_owned(),
);
let rate_limiter = rate_limiter.clone();
App::new()
.wrap(middleware::Logger::default())
.app_data(Data::new(context))
// The routes
.configure(|cfg| api_routes::config(cfg, &rate_limiter))
.configure(|cfg| lemmy_apub::http::routes::config(cfg, &settings))
.configure(feeds::config)
.configure(|cfg| images::config(cfg, &rate_limiter))
.configure(nodeinfo::config)
.configure(|cfg| webfinger::config(cfg, &settings))
})
.bind((settings_bind.bind, settings_bind.port))?
.run()
.await?;
Learn Rust by looking at Lemmy.
Let’s take apart Lemmy and see how it works.
This is a cool idea for a community
So I think I figured a few things out.
HttpServer::new(move || {...})
is doing a few things. First,HttpServer
is calling a methodnew
whichmove
s the context (settings_bind
) into a closure ({...}
), which is kind of like a local-use function. The||
denotes that no variables are being passed into the closure. Instead, the context is beingmove
d in so I guess it can be destroyed once the scope ofHttpServer
disappears.App::new()
must refer to a method ofHttpServer
but I haven’t investigated that yet. I suspect.run()
applies theApp
construct, whatever it is.new
isn’t a method andHttpServer
isn’t calling it.new
is an associated function of whateverHttpServer
is. Associated functions called new are usually used to create a new “instance” of a type they are associated with. SoType::new
will usually return aType
, aResult<Type, _>
, aOption<Type>
or something similar.Methods are functions that operate on a type, they always have
self
or&self
or&mut self
or whatever as their first parameter. You can omit that parameter if you use the “dot-syntax”.The context (I assume you mean the variable in the closure) isn’t moved anywhere, the
move
keyword means, that the closure will take the ownership of all variables from the outer scope mentioned inside of it instead of borrowing or copying them.What can be destroyed? In most cases you don’t need to destroy (free/uninit) anything in rust, that is done “automatically” when something goes out of scope.
It’s an associated function of
App
.It’s a method of whatever
.bind((settings_bind.bind, settings_bind.port))?
returns.You should really take @nutomic@lemmy.ml’s advice and read the book, play around a little bit and maybe do the rustlings course before you try understanding more complex programs. It’s not super hard but it will take some time, but feel free to ask here or in the rust forums if you feel stuck.
This is a nice idea, but I dont think its the best way to learn. I suggest you start by reading the Rust Book, and writing some code yourself. That code in your post is using actix, so have a look at their docs and examples, and maybe also write a small web server yourself to play around with.
I’m going to tackle Rust from a variety of angles, one of which is by studying Lemmy.
Initial observations
HttpServer::new(move || { [1] }).bind[2].run().await?;
is the basic structure. [1] consists of two parts,context
andrate_limiter
variablesApp:new()
We see
context
being loaded as.app_data
inApp::new()
, andrate_limiter
being loaded into the config.Then routes are added.
Part of what is unusual to me is defining variables within the server setup. But this may have something to do with the way Rust deals with variables. Since
context
has no meaning outside of this setup, better to use it here and throw it away once we’re done setting up theHttpServer
?Whereas
App::new()
dealt with Lemmy specific app configuration and behavior,.bind
appears to involve more genericHttpServer
configuration stuff [2], including the port to bind to.Then the whole thing
.run()
s, and then.await?
s for multithreading.