It's because it's written using a low level library. Here's the first two sections (the http handling part) in Rocket:
#![feature(plugin, custom_derive)]
#![plugin(rocket_codegen)]
extern crate rocket;
use rocket::request::Form;
#[derive(FromForm, Debug)]
struct NewMessage {
username: String,
message: String,
}
#[derive(FromForm, Debug)]
struct TimeRange {
before: Option<i64>,
after: Option<i64>,
}
#[post("/", data="<message>")]
fn message_create(message: Form<NewMessage>) -> String {
format!("{:?}", message)
}
#[get("/?<times>")]
fn message_query(times: TimeRange) -> String {
format!("{:?}", times)
}
fn main() {
rocket::ignite()
.mount("/", routes![message_create, message_query])
.launch();
}edit:
And no I'm not talking about the borrow-checker, I like that and it took very little time to figure out.
Timeframe: fairly soon. You can already try out most of it on nightly. There's still some final details to work through though.
Before we get into details, none of these things are breaking changes; in Rust 2015, we will add lints to nudge you in this direction, in Rust 2018, those will move to deny by default. This means if you truly love the old system, you can still use it, by allowing instead of denying the warnings.
Main ideas:
* absolute paths begin with the crate name, or "crate" if they're paths in the same crate.
One of the core issues that these changes address is that, if you don't develop the right mental model around defining items and use, counter-intuitive results can happen. For example:
extern crate futures;
mod submodule {
// this works!
use futures::Future;
// so why doesn't this work?
fn my_poll() -> futures::Poll { ... }
}
std is even worse, as you don't even write the `use` or `extern crate` lines: fn main() {
// this works
let five = std::sync::Arc::new(5);
}
mod submodle {
fn function() {
// ... so why doesn't this work
let five = std::sync::Arc::new(5);
}
}
Quoting the RFC:> In other words, while there are simple and consistent rules defining the module system, their consequences can feel inconsistent, counterintuitive and mysterious.
With the changes, the code looks like this:
extern crate futures;
mod submodule {
use futures::Future;
fn my_poll() -> futures::Poll { ... }
}
fn main() {
let five = std::sync::Arc::new(5);
}
mod submodle {
fn function() {
let five = std::sync::Arc::new(5);
}
}
Nice and consistent.That being said, there's also some discussion here that hasn't been totally sorted. Using the crate name in this way has some technical problems, and so we might make it that it's "extern::crate_name", so that is
mod submodule {
use extern::futures::Future;
This is a bit verbose though, so we're not sure that's what we want. See the end of this post for that discussion.* "extern crate" goes away.
Speaking of the code above, why do we have to write `extern crate futures` anyway? It's already in your Cargo.toml. Cargo already passes --extern futures=/path/to/futures.rlib to rustc. In the end, it just feels like boilerplate. Again, there's that inconsistency between std and futures in the code above. Removing the extern crate line makes it more consistent, and removes boilerplate. 99% of the time, people put the line in the crate root anyway, and half of the 1% who don't get confused when it doesn't work when they do this.
* The "crate" keyword can be used like pub(crate) can today, for making something crate-visible but not public externally
This feels superficial, but ends up also being a much easier mental model. Here's the problem: you see "pub struct Foo;". Is Foo part of your public API, or not? Only if it's in a public module itself! pub(crate) is longer than just crate, and is often the thing you actually want when you use 'pub' inside something that's not public. So let's encourage the right thing, and one that's easier to tell at a glance.
* mod.rs is no longer needed; foo.rs and foo/ work together, rather than foo/mod.rs
There's tons of awkwardness here. Most people use foo.rs until they give it a submodule, then they have to move it to foo/mod.rs. This just feels like meaningless change for no reason. Instead of "mod foo; means look in foo.rs or foo/mod.rs", it becomes "mod foo; means foo.rs". Much more straightforward. Same with a "mod bar" inside foo.rs, it becomes foo/bar.rs, (well, as it is today, but you can see how this is more consistent overall. If it had submodules, it might be foo/bar/mod.rs!)
Also, if you have a bunch of `mod.rs` files open in your editor, you have no idea what module they corresponds to, as they all say `mod.rs`. Now they'll say the file name instead.
----------------------------
That's the quick summary. I've left out some details. If you want to try this yourself, grab a nightly and add this:
#![feature(
crate_in_paths,
decl_macro,
extern_in_paths,
crate_visibility_modifier,
)]
Note this includes the verbose "use extern" stuff.If you'd like to read the details yourself: https://github.com/rust-lang/rfcs/blob/master/text/2126-path... and https://internals.rust-lang.org/t/the-great-module-adventure... ; the former is the RFC that was accepted, the latter is the discussion about the extern issue, with a few different variants.
While that would make you unnecessarily build the dependency the first time, it at least wouldn't be in your final binary, since everything would be unused. That said, we could still warn about it anyway, even without extern crate.
I'm actually most hyped about `#![feature(crate_visibility_modifier)]` to be honest. I know it's essentially just an alias for pub(crate), but I'm all about typing less parens in my item definitions! I didn't know about the other `pub(...)` modifiers for the longest time, but they've been so useful for things like games programming, where the entire point of the exercise boils down to "cross cutting concerns" and "eh you've got a &mut Player anyways, just reach in there and poke at the state!"
The `../mod.rs` change is also quite nice. I mean, at the end of the day it'll only save me a `mv` command and a refresh of my directory listing in vim, but sometimes those small context switches can have a surprisingly large impact on flow; since now I'm thinking about filesystems and module trees rather than the problem at hand.
> I mean, at the end of the day it'll only save me a `mv` command
Yeah, as you say, it feels minor, but hopefully, a lot of tiny ergonomic changes will end up feeling significantly better. It's also why the epoch concept is important; it gives us a way to talk about how all these little changes every six weeks build into something much bigger and nicer.
This is probably the most important reason to make the change tbh. It doesn't seem like a big thing but it's one of those ergonomic papercuts that will make the user experience subtly better once it's fixed.
import QtQuick 2.7
This has multiple benefits:* You only need to go to up to 1 file to add an import * Tools like cargo-script don't need special comment syntax for inline dependency specification. * The source code functionality arguably depends on the version of the libraries included as well as the name, so it keeps it together.
This seems pretty obvious though so I'm guessing there's a reason it wasn't done?
Another way to think of it is, Cargo.toml contains all metadata about the build, and this is fundamentally metadata.
Often I'm writing code that's as high-level as other languages. Though by its nature it also has aspects that you don't even have to think about in other languages, like Fn vs FnOnce vs FnMut when working with closures. So it's undoubtably going to be more difficult than other languages.
For example, I think most Rust users would agree that it could be confusing when a fn returns a Cow<&str> vs OsString vs String. But it's straight-forward to convert those into String even if you don't care what the differences are.
I think it's fair to simply not have an appetite for a certain language's set of idiosyncrasies.
Your negative is my positive, I can transform a &[u8] to &str without any allocations for instance.
I really think people need to get over syntax. I don't find rust particularly good looking either, but I get over that because I like the semantics and it sits in a nice place in the ecosystem of programming languages. Likewise i don't like how C# uses PascalCase everywhere, or puts opening braces on its own line - yet when I program in C#, I adhere to those conventions.
Syntax is incredibly subjective, and also superficial. If you think the semantics of a language suck or are not a fit for what you are doing - that's a reasonable conversation. But you really shouldn't limit your language choice based on non-alphanumeric characters or whatever your objection is.
I like the semantics of Rust, I actually use Rust, and plan on continuing to do so. I still hate the syntax and module system though.
Compared to what? Compared to Python, JS, Go, etc, yes, those are different tools for projects with different requirements, that allow for a leaner syntax. Compared to other languages of it's category (non garbage collected, manual memory management), Rust looks alright, IMHO.
I think generalizations such as this one don't make much sense. But I agree with the rest of what you say.
Like someone hijacking Haskell discussion because they didn't grasp functional programming and don't see how anyone else could.
I'm writing most of my new code in Rust that I would've written in Node or Go. Sometimes it makes more sense to use something else. So what?
Maybe it's time to leave the theater so others can enjoy the show. Your "me no likey" posts have kinda overstayed their welcome without advancing the discussion.
I wrote a working Macho-o Parser in rust and I plan on continuing the symbolic execution engine I started in rust, so I'm not a complete beginner to rust. Also there is such a thing as the best tool for the job. And for web services I don't think rust is that tool of choice.
> Like someone hijacking Haskell discussion because they didn't grasp functional programming and don't see how anyone else could.
I understand why rust exist and it makes sense, a safe low level systems programing language. What I don't get is why one would use rust for the vast majority of microservices. There are easier more productive tools available.
> I'm writing most of my new code in Rust that I would've written in Node or Go. Sometimes it makes more sense to use something else. So what?
So you acknowledge that there are better tools for building microservices but your defense for using rust is "so what"? I mean that's fine for personal projects but when your putting things in production you should be using the best tools available.
> Maybe it's time to leave the theater so others can enjoy the show. Your "me no likey" posts have kinda overstayed their welcome without advancing the discussion.
Lol, so only positive opinions of rust are allowed here? But seriously, steveklabnik responded to one of my post with so much useful and awesome information that your, I need to leave post is ridiculous.
The language is a tiny piece of the end-to-end development experience.
Now, for playing around on a ESP32? C++ or Rust (when available).
A Python equivalent using the typing module:
@app.get("/<times>")
def message_query(times: TimeRange) -> str:
return format("{:?}", times)You should check out Rocket or Actix-Web or Gotham for something a bit more higher-level. Also know that it's Rust we're talking about - it's an expressive languages, but it's also a systems language, if you want something you can toss in a breakpoint and start introspecting or experimenting you'll probably want Ruby or Elixir.
extern crate actix_web;
use actix_web::*;
fn index(req: HttpRequest) -> String {
format!("Hello {}!", &req.match_info()["name"])
}
fn main() {
HttpServer::new(
|| Application::new()
.resource("/{name}", |r| r.f(index)))
.bind("127.0.0.1:8080").unwrap()
.run();
}