Sapper, a lightweight web framework written in Rust
github.com
github.com
There is much experimentation going on right now, with cool little innovations here and there, but I have yet to see an approach that looks really new and Rust-y to me.
For example: Rust's type system certainly has some nice properties to track dependencies between middlewares[^1] but I'm not convinced middlewares are even the best approach possible here. Also, it will be interesting how a framework based on async IO will look and what tricks/syntactic sugar/macros will be employed to keep boilerplate code down.
This is a pretty big order, of course: While I enjoy using small libraries, a well designed web framework requires a lot of organisational effort for all the small pieces to fit together nicely. I think most of the 'small pieces' are already there or at least well under way, by the way. For example: The async (mio) branch of hyper looks pretty solid for HTTP1/2, the Diesel ORM is fantastic (and will, according to creator Sean Griffin, soon hit 1.0), and handling JSON will only get better with future versions of serde.
[^1]: As recently discussed in https://chrismorgan.info/blog/tween.html and some other posts concerning the use of sessions types in Rust.
There's a good discussion of this on /r/rust: https://www.reddit.com/r/rust/comments/4lz92j/tween_a_middle...
I'm pretty interested in tomakas's approach, which seems to avoid the approach of middlewares, in favor of explicitness: https://www.reddit.com/r/rust/comments/4lz92j/tween_a_middle...
Also fun fact: we had a small amount of downtime today :( Type safety can't protect you from everything. https://users.rust-lang.org/t/crates-io-is-down-fixed/6060/6...
public void someOperation() throws SomeException { ... }fn something() -> (String, String) ...
let (result, err) = something()
It's just that Result is preferable because you can pattern match on it
fn something() -> (Option<String>, Option<String>)
Because the Rust types aren't nullable by default. But that more clearly demonstrates that Result types are better since they enforce that the error and result are mutually exclusive.Null is a value that indicates the absence of something else that could be there instead, Unit is essentially the lack of any value. So when a python function implicitly returns None whenever it's called, that's semantically more similar to Unit than Null.
Edit: I see now that you're talking about the difference between null and Result/Option, not the difference between null/Option and Unit, so we're talking about different things.
Semantically, a value that could be data or null is equivalent to an option type. Option types have methods, yes, but that's just a superficial convenience facility, you could implement the same thing for nullable types by writing a function:
public static <A, B> B map(A object, Function<A, B> f) {
if (object == null) return null;
return f.apply(object);
} >>> type(())
<class 'tuple'>`Ok(value)` makes an instance of the `Result` type. Imagine `struct Result Ok(some_t value);` in C.
`()` is a 0-length case of a tuple. Sort-of like `{}` in C's `int arr[] = {}`.
It's used here, because `Ok` is defined to take a value, so something must be provided. The compiler knows how to optimize out `()` completely, so the single `Result` type can be used for efficiently handling both cases returning a something||error and NULL||error.
pub fn from_bytes(bytes: &'a [u8]) -> IResult<&'a [u8], C<'a>> { ... }
or something like this:
fn parse_response<'a>(bytes: &'a [u8]) -> IResult<&'a [u8], C<'a>> { switch!(bytes, tuple!(be_u32, be_u32), (::CONNECT_ACTION_ID, tid) => map!(be_u64, |cid| B::new(tid, ResponseType::Connect(cid)) ) | (::ANNOUNCE_IPV4_ACTION_ID, tid) => map!(call!(W::from_bytes_v4), |ann_res| { C::new(tid, ResponseType::Announce(ann_res)) }) |
"from_bytes is a public function with one lifetime parameter, a. It takes one argument, a slice of u8 with the lifetime 'a, and returns an IResult, which gives back a slice of u8 with the lifetime 'a in the success case, and some type C, which is also parameterized with the lifetime 'a, in the failure case."
This is not really normal rust code; it's nom, which is a very macro-heavy DSL. There are decent upsides, and I love nom, but its downside is that it does look a bit odd.
Have there been any (recent) discussions on how to make this stuff friendlier? I mean, one could always just bootstrap lisp on top of ecl[1], like clasp[2] does, and either generate rust code, or just machine code -- but even for plain rust it seems it should be possible to this stuff in a friendlier way?
I'm wondering if it wouldn't be worth it to have a more verbose syntax and/or keywords for this stuff -- it really does appear to me as a usability barrier -- not just because it's new and strange. Reminds me a bit about perl's use of "sigils" which I never quite thought made a lot of sense personally. I'm firmly in the camp that if you want more syntax than common lisp or smalltalk, you need a really good reason for every bit you add. And while I like a lot of things about rust, I'm starting to wonder if this isn't an area that really needs improving. Kind of like how C++14 tries very hard to get rid of a lot of stuff.
[1] "Embeddable Common Lisp" https://common-lisp.net/project/ecl/
"Just embed a Lisp" is very far from a "just", and, most non-lispers find Lisp very hard to read, specifically because there is no syntax.
fn parse_response<'a>(bytes: &'a [u8]) ->
IResult<&'a [u8], TrackerResponse<'a>>
{
switch!(bytes, tuple!(be_u32, be_u32),
(::CONNECT_ACTION_ID, tid) =>
map!(be_u64, |cid|
TrackerResponse::new(tid,
ResponseType::Connect(cid))
) | (...)
Appears to be very dense in very terse type and life-time information, compared to the variable names, function calls/macro expansions.I get that it might be necessary at times, I just think it is a worthy goal to strive to make code look somewhat simple, even when doing complex things.
Example, in this case, with all the lifetimes(?) being the same(?) 'a, could perhaps the compiler infer that? Would it be more useful to make it explicit when they differ?
That kind of thing.
[ed: As or it being ironic, I see powerful constructs like macros more as a tool for making complex things simpler, not to complicate simple things.
That's what I mean when I say "macro-heavy" code being hard to read is ironic. It could of course be that this example really is complex, but it kind of strikes me as looking more "complected" than "complex".]
> I just think it is a worthy goal to strive to make code
> look somewhat simple,
To be clear, I do as well. Some things are just inherently complex, though. > could perhaps the compiler infer that?
The only reason they're added here is that they're not able to be inferred, and the reason they're all the same is that you're explicitly connecting the lifetime of each of these things. > it kind of strikes me as looking more "complected" than "complex".
nom is for building extremely fast, extremely low-overhead parsers. When you're trying to do stuff like that, you can't always afford convenience stuff. Consider if you had to write all the code the macros generate by hand!Is the default of having them all be implicitly different, or unlinked, lifetimes very useful?
We used to only have the first of those three rules. When we added the other two, almost 90% of lifetime annotations in function declarations were able to be removed from the compiler and standard library.
Also, I knew this piece of code looked familiar. For those that wanted to see this code with whitespace, https://github.com/GGist/bip-rs/blob/master/bip_utracker/src....
That said, I was a systems engineer and did a lot of C++ but now don't really understand it anymore because these days it's incomprehensible to me especially after c++11/14 (and I coded in c++ for many years!).
Same goes for javascript - with es7, the language syntax is changing very much (and imo, not for the better). They just keep adding features for no reason.
I just hate that languages at some point try to behave they are some sort of library with non-stop changes :/ So, despite not liking Go syntax, I like the people behind Go. They don't want to change it too much for no reason (yes yes, we need generics). I like that philosophy.
In fact, C is pretty much the only language which has held on to it's core syntax to me.
Also, I'm assuming the windows support is good these days?
For working with Windows APIs, see https://github.com/retep998/winapi-rs
Fortunately for us, they also put plenty of effort into making it work on less-popular platforms like my Linux desktop. I'm grateful even if it's lower priority. :)
Note: I'd be curious what OS's most Firefox and Rust developers use.
http://blog.rust-lang.org/2016/05/09/survey.html
I think it's about to close in the next couple of days. wink wink nudge nudge
I don't use a lot of other mozilla software except rust (a little) and from what I remember they had spotty support for Windows when Rust first came out, but this is true of pretty much all new languages unless Microsoft creates it.
"but I believe a lot of the developers respect open source philosophy and therefore we get first class software for opensource operating systems"
That's probably true to some degree. That the least, there might be more developers on FOSS boxes testing it which spots more problems. Hypothetical as I have no data on Rust to test it with.
Do you have any numbers on the "faster" aspect? Most comparisons I've seen put the Linux version solidly behind Windows version.
As for features, for example video playback on Linux Firefox has severely lagged behind Windows version. It's not very long time ago that I needed to make some "non-recommended" about:config changes to get youtube working.
Even integration-wise I think I have had more problems on Linux than on Windows, file associations, copy-paste, and misapplied themes being most notable issues that come to mind.
I'm pretty sure that I've seen even Mozilla say that majority of developer effort goes to Windows first, and it shows. While you anecdotally might have had good luck, I'd claim that is atypical
To answer your question about OSes, most Mozilla developers who I've met run Linux or Mac OS on their machines, but they also try to use Windows sometimes because Windows is such an important desktop platform.
1.) You need to `cargo install racer` and add the bin to your path
2.) Clone the Rust source of your version of Rust, and add that to your path
3.) Install the plugin and set the paths in Atom
Personally I've found visual studio code to have a good mix of light weight and functional.
* atom-beautify (with rustfmt installed, `cargo install rustfmt`)
* language-rust
* linter + linter-rust (using cargo check, `cargo install cargo-check`)
* you-complete-me (but YCM is not fun to install, so I'd recommend the racer plugin, both require the Rust source cloned and installed somewhere)
Altogether, those plugins give a pretty good Rust experience.Also, I am very closely following the progress of the rust intellij project, it's coming along nicely.