[HN Gopher] Fast Rust Builds
       ___________________________________________________________________
        
       Fast Rust Builds
        
       Author : agluszak
       Score  : 177 points
       Date   : 2021-09-05 15:36 UTC (7 hours ago)
        
 (HTM) web link (matklad.github.io)
 (TXT) w3m dump (matklad.github.io)
        
       | nextaccountic wrote:
       | > If you do need generic interface for better type-safety and
       | ergonomics, make sure that the interface layer is thin, and that
       | it immediately delegates to a non-generic implementation. The
       | classical example to internalize here are various functions from
       | str::fs module which operate on paths:                   pub fn
       | read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
       | fn inner(path: &Path) -> io::Result<Vec<u8>> {             let
       | mut file = File::open(path)?;             let mut bytes =
       | Vec::new();             file.read_to_end(&mut bytes)?;
       | Ok(bytes)           }           inner(path.as_ref())         }
       | 
       | --
       | 
       | Nobody seems to care, but there's a crate that does this for you
       | https://github.com/llogiq/momo
       | 
       | But the Rust compiler should be doing this automatically :(
        
         | epage wrote:
         | There is interest in making this automatic, its just not been
         | the highest priority for people.
        
           | mrec wrote:
           | https://github.com/rust-lang/rust/issues/77960 I think.
        
         | zozbot234 wrote:
         | > But the Rust compiler should be doing this automatically
         | 
         | This will become a lot easier once the actual full const-
         | generics implementation lands. Because then even your "non-
         | generic" implementations will be enabled to depend on compile-
         | time-defined features of the instantiated type (such as size,
         | alignment, value bounds etc.) without any sort of duplicate
         | code generation. Of course, these in turn should delegate to
         | truly non-generic code when feasible to minimize bloat.
        
         | jstx1 wrote:
         | As a side note, looking at this block of code, the syntax is
         | just so ugly and overcrowded. I don't get how a relatively new
         | language ended up looking so messy so fast; it's like no one
         | cared about readability.
        
           | scottlamb wrote:
           | Partly personal preference in both what is readable and in
           | how much you optimize caller convenience vs simple callee
           | code. I think it's fine to just expect the caller to do the
           | conversion. In fact, it's often automatic. (Eg if you have a
           | PathBuf, then just sticking a & in front of it is sufficient
           | to supply &Path via Deref<Target = Path>.) So I'd write this
           | function as follows:                   fn read(path: &Path)
           | -> io::Result<Vec<u8>> {             let mut file =
           | File::open(path)?;             let mut bytes = Vec::new();
           | file.read_to_end(&mut bytes)?;             Ok(bytes)
           | }
           | 
           | which I find readable. Comparing to another language...I
           | haven't written Go in a while, but I think the rough
           | equivalent would be:                   func Read(path *Path)
           | ([]byte, error) {             file, err := os.Open(path)
           | if err != nil {                 return nil, err             }
           | slice, err := io.ReadAll(file)             if err != nil {
           | return nil, err             }             return slice, nil
           | }
           | 
           | (Maybe in this case you'd just return the io.ReadAll rather
           | than doing an if, but I'm trying to use similar constructs.
           | The ? is something you can use all throughout the function,
           | not just at the end, so I tried to match that pattern.)
           | 
           | [edited: I said there wasn't a io.ReadToEnd extracted from
           | io.ReadFile in Go, but jatone pointed out it's called
           | io.ReadAll.]
        
             | jatone wrote:
             | bytes, err := ioutil.ReadAll("path.txt")
             | 
             | is the golang equivalent.
        
               | [deleted]
        
               | steveklabnik wrote:
               | To be clear you can write                   let bytes =
               | std::fs::read("path.txt")?;
               | 
               | If you're trying to actually do this in real code. The
               | example is how you'd write this function yourself, but if
               | you're just trying to Get It Done, it's significantly
               | easier than the example.
        
             | oxnrtr wrote:
             | fun read(path: &Path): io.Result[Vec[UInt8] {           var
             | file = File.open(path).orReturnErr()           var bytes =
             | Vec.new()           file.readToEnd(&mut
             | bytes).orReturnErr()           Ok(bytes)         }
             | 
             | would be good enough for me, tbh.
        
               | yakubin wrote:
               | _> File.open(path).orReturnErr()_
               | 
               | Method syntax is reserved for functions. There are no
               | method syntax macros. Since a return in a function
               | terminates execution of this function, and not its
               | caller, your example would not work.
        
             | capableweb wrote:
             | I don't know Rust but converting that code snippet of Go
             | code you wrote to (non-idiomatic [wouldn't use upper-case
             | characters, namespaces used doesn't actually exists])
             | Clojure:                   (defn Read [path]
             | (io/ReadToEnd (os/Open path)))
             | 
             | Now that's what I call readable!
        
               | scottlamb wrote:
               | You can chain calls in Rust. If read_to_end were written
               | to allocate and return a fresh Vec, you could do this:
               | File::open(path)?.read_to_end()
               | 
               | but instead read_to_end is written to take a presupplied
               | Vec so the caller has the option to reduce allocations.
        
               | brundolf wrote:
               | Clojure is also a much higher-level language than Rust
               | (and arguably Go too)
        
           | tanaypingalkar wrote:
           | I think rust syntax is more readable and self explains.
           | Having syntax more minimal, more use of symbols, spaces and
           | tab make them beautiful but they are really hard to get if it
           | is written by someone else. I personally noticed it's more
           | easy understand other rust project than other langs project.
        
           | mamcx wrote:
           | The syntax is ugly and can become overcrowded, but is in the
           | HIGH side of readable.
           | 
           | Compare:                   def read(path):
           | 
           | What is path? Unknow. What it read? Unknow. What it return?
           | Unknow                   pub fn read<P: AsRef<Path>>(path: P)
           | -> io::Result<Vec<u8>> {
           | 
           | What is path? Anything that can become path on the file
           | system.
           | 
           | What it read? Anything that can be pointed with that PATH
           | 
           | What it return? Vec of bytes or a IO::Error
           | 
           | PLUS: This code is compiled tot he exact types that call it,
           | Path not allocate, the result can be cloned, the errors are
           | defined in IO, ....
           | 
           | --
           | 
           | Rust is very readable: Almost any line tell you what is
           | happening and what expect.
           | 
           | Where it get messy is when it hide that crucial info. But is
           | not many times it happens.
        
           | nextaccountic wrote:
           | The worst decision regarding Rust syntax was following C++
           | syntax. Due to this, we write, in type syntax
           | 
           | Things<Like<This, That>, That> -- as in C++
           | 
           | Instead of a more legible (and very familiar to Rust authors)
           | 
           | Things (Like This That) That -- as in Haskell
           | 
           | Inherited from C++, we had also :: for namespacing (while a
           | dot . could be perfectly be used for that), and tons of other
           | stuff like <> for scoping generics.
           | 
           | Without some of that we could have:                   pub fn
           | read<P: AsRef Path>(path: P) -> io.Result (Vec u8) {
           | fn inner(path: &Path) -> io.Result (Vec u8) {             let
           | mut file = File.open(path)?;             let mut bytes =
           | Vec.new();             file.read_to_end(&mut bytes)?;
           | Ok(bytes)           }           inner(path.as_ref())
           | }
           | 
           | Perhaps it looks a little better? It looks for me, at least.
           | 
           | For doing better than that we could drop C-like syntax
           | perhaps?
           | 
           | The trouble is that languages have a weirdness budget -
           | there's a finite amount of novel stuff a language can have
           | until it becomes too weird for mainstream programmers. So
           | language designers have to pick their weird features
           | carefully. Spending it all in novel syntax is unwise.
           | 
           | I think that at the time the Rust pull was safety for low
           | level code and, as Rust was targeted to replace some C++
           | components, following C++ syntax was seen as a good idea.
        
             | the_mitsuhiko wrote:
             | What's the worst decision for you is my favorite feature of
             | the language syntax for me. I find it easy to read and very
             | easy to navigate in the text editor because types are
             | enclosed by balanced operators that editors can easily
             | highlight and jump between.
        
             | axelf4 wrote:
             | Surely mixing usage of space and parentheses for function
             | application is a bad idea?
        
               | nextaccountic wrote:
               | But I'm not talking about function application - I'm
               | talking about type application.
               | 
               | The syntax of type application and function application
               | is already different in rust: F<A, B> vs f(a, b).
               | 
               | Type application even has default and named parameters!
               | F<X = A, Y = B> and F<A> if B has a default.
               | 
               | I just think this particular type-level syntax is very
               | noisy, specially because types in Rust tend to be very
               | nested.
        
             | xpressvideoz wrote:
             | > Rust was targeted to replace some C++ components,
             | following C++ syntax was seen as a good idea.
             | 
             | And that was a good decision. If it were too far from the
             | C++ syntax, I would not have learned it, because as a
             | mostly C++ guy at the time, some similarities with C++
             | helped me keep interest in Rust. Also, I was unable to
             | parse Haskell's "type parameters as function applications"
             | syntax, which was purely confusing. The Rust syntax is an
             | abomination, that's true, but it is a necessary evil.
        
             | Zababa wrote:
             | I like how the namespace and the function chaining have
             | different characters. I think it makes it easier to read.
        
             | darksaints wrote:
             | Function application and type annotation both using parens
             | is pretty jarring for a lot of people. I much preferred the
             | proposal that would have used [] like typescript or scala.
        
             | brundolf wrote:
             | Dot syntax is used when accessing a function/property on a
             | _value_ , :: is used when accessing a function/property on
             | a _module or type_. I think it 's great having this visual
             | distinction, especially given that you can call the same
             | function in either style:                 let x =
             | Foo::new();            x.bar();       Foo::bar(x);
        
               | Jare wrote:
               | I thought the distinction was meaningful until I worked
               | in a language that used dot pervasively, and saw that it
               | was totally fine.
        
             | oconnor663 wrote:
             | By comparison, near-source-compatibility with C is arguably
             | the "worst" design decision that C++ made, a constant
             | source of complexity and frustration, and yet the #1 reason
             | behind C++'s early growth and continued success. There are
             | a lot of different factors at play here.
        
           | OJFord wrote:
           | To take a guess at what you don't like, it can be
           | equivalently spaced out a bit more like:
           | pub fn read<P>(path: P) -> io::Result<Vec<u8>>         where
           | P: AsRef<Path>,         {
           | 
           | And of course if you were using a lot of u8 vec results, you
           | might create a shorter alias for that.
        
           | pcwalton wrote:
           | Come on, it is definitely not the case that nobody cared
           | about readability. My sleepless nights after arguments about
           | syntax in 2011 and 2012 very much say otherwise.
        
             | steveklabnik wrote:
             | Or the endless arguments about await, or that time I cried
             | because the discussion over what we call integers was so
             | contentious. We all cared quite a bit about syntax. Heck
             | that's partially why almost none of it is novel!
        
               | matklad wrote:
               | I don't think that when people say "Rust has ugly syntax"
               | they mean that Rust has ugly syntax. It seems that what
               | they are complaining about is actually Rust semantics,
               | which needs extra syntax to express it.
               | 
               | Blog post idea: express this snippet (with references,
               | aliasing control, generics and all) in pseudo C++, Java,
               | Python, etc. Then express "reading a file in gc language
               | with exceptions" in pseudo Rust.
        
           | capableweb wrote:
           | It is what usually happens when you design by committee
           | instead of everyone having a common understanding and working
           | towards the same goal under a single person/entity.
           | 
           | Not saying it's good or bad, just how Rust ended up where it
           | is.
        
             | pcwalton wrote:
             | What Rust committee?
        
               | capableweb wrote:
               | "Design by committee" -
               | https://en.wikipedia.org/wiki/Design_by_committee
               | 
               | I disagree it's a pejorative term as Wikipedia puts it
               | though. Design by committee is in many cases necessary or
               | even wanted (standard bodies).
        
             | [deleted]
        
           | Zababa wrote:
           | I find the syntax very readable. What's your problem with it?
        
             | kbenson wrote:
             | I imagine it's that there's a fairly high ratio if function
             | signatures to other lines of code, and function signatures
             | are particularly gnarly in rust compared to many other
             | languages, at least when lifetimes are involved.
        
               | Zababa wrote:
               | I agree that I find lifetimes a bit hard to read, but
               | outside of that I usually find Rust's syntax really clear
               | for an algol-like. For example, they use the ML-style
               | type declaration variable: type, which I find more
               | readable than the C-like type variable. There's a
               | different syntax for namespace and function chaining,
               | which I also like.
        
               | infogulch wrote:
               | I wonder if this is a side-effect of rust function
               | signatures being very _precise_ , as in the function is
               | very clear about the allowable type and ownership of its
               | parameters and return values, where most languages can't
               | even express things at that level of precision so
               | people's eyes glaze over when reading it.
        
               | kbenson wrote:
               | I imagine so. A function signature is rust encodes more
               | information than a lot of other languages. From my (very)
               | limited experience, without lifetimes they look fairly
               | similar to C++, and then adding lifetimes just makes them
               | busier and more complex, but that's because you're
               | encoding additional information.
        
             | jakobnissen wrote:
             | Compare to Julia                   # Your big function has
             | specific types         function my_function(x::UInt,
             | y::UInt)             [ ... ]         end              #
             | Wrapper function to minimize monomorph. cost
             | my_function(x::Integer, y::Integer) = my_function(UInt(x),
             | UInt(y))
        
               | nextaccountic wrote:
               | Rust need a lot of extra stuff in function signatures to
               | do its magic. Julia couldn't possibly do what Rust does
               | because it doesn't have enough information
        
               | jakobnissen wrote:
               | No. They didn't have to have a pub keyword, which takes
               | space. They didn't have to have the ugly syntax to make
               | functions generic: Julia's functions are generic without
               | using those brackets. Rust didn't have to have explicitly
               | needed return types, or having to have those fully
               | specified.
        
               | dthul wrote:
               | Fully specified function signatures are a conscious
               | decision because it allows you to type check and borrow
               | check a piece of code without needing to analyze the
               | bodies of other functions. I also love it as a human
               | because there is no guesswork involved.
        
               | nextaccountic wrote:
               | Rust generics can be written in a more compact way,
               | fn f(x: impl Test) { ... }
               | 
               | Is basically the same as                   fn f<T:
               | Test>(x: T) { ... }
               | 
               | Having to annotate the return value of functions is
               | partly to make sure don't break semver accidentally, but
               | it also constrains type inference to a local search
               | rather than global search, avoiding spooky action at
               | distance (when modifying a function changes the signature
               | of another function). It seems that semver hazards are
               | more important in languages with static types.
               | 
               | That pub? It's punching waaay above its weight! Having
               | things private by default protects against semver hazards
               | too, but it's also the major tool that enable writing
               | safe code that uses unsafe { .. } blocks underneath.
               | That's because a single unsafe block typically taints the
               | whole module it's contained (such that even safe code
               | needs a safety review). Being able to write safe
               | abstractions of unsafe code is the pull of Rust so people
               | need to get it right.
               | 
               | For example, setting the field len of a Vec to a value
               | larger than what's initialized in its buffer will cause
               | UB when accessing that memory; if this field weren't
               | private, making a safe Vec would be impossible (likewise,
               | a function that could set the len to any value must be
               | private, etc).
               | 
               | So pub can only be used for things that are safe to
               | export, and people should be careful with that.
               | 
               | Here's my least favorite syntax from there: that damn
               | Ok(..). Ok-wrapping sucks. There were proposals to remove
               | it, but they were rejected. There's a library that
               | removes it [0], and people should use it. It also makes
               | the Result<..> return type implicit in the function
               | signature too.
               | 
               | [0] https://boats.gitlab.io/blog/post/failure-to-
               | fehler/#but-abo...
        
               | Zababa wrote:
               | So how do you make the difference between public and
               | private code without a pub keyword? And for the "explicit
               | return types", I think it makes the code more readable.
               | You seem to think that what is your personal preference
               | (something that looks like Julia) is best practice. It's
               | not, it's just a preference.
        
               | jakobnissen wrote:
               | Both Julia and Python has a concept of public and private
               | code, neither have a pub keyword.
               | 
               | I don't believe Julia or Python is "best practice" over
               | Rust. I even empathize with the opinion that explicitness
               | may be preferential sometimes. But you _must_ be able to
               | see how Rust's syntax overload obfuscates the core
               | business logic, right?
        
               | Zababa wrote:
               | The difference between pub in Rust and _/__ in Python
               | isn't that big. It's like Go that uses a capital letter
               | to say what code is public. There's also a difference in
               | philosophy between public by default and private by
               | default. All in all, it's a small detail and doesn't
               | affect things.
               | 
               | Sure, business logic is more readable in something like
               | OCaml than in Rust. On the other hand, Rust isn't really
               | worse than something like Java, and I like having more
               | information than what you have in Python. I'll also add
               | that Rust is often chosen for speed and reliability, and
               | that part of that syntax is here to guarantee that. It's
               | not directly business logic in the traditional sense, but
               | it's something you expect from your code.
               | 
               | All in all, Rust is still not at the level of the
               | "platonic ideal for syntaxes", especially not for most of
               | the code I write (business logic for web apps, which
               | wouldn't need Rust's speed). Still, in the realms of
               | programming languages that are used, it's one of the
               | best.
        
               | Ar-Curunir wrote:
               | Those are all trade-offs. Ditching fully-specified
               | function signatures makes type inference more difficult
               | and results in poorer type inference errors
        
               | Zababa wrote:
               | Your comparaison isn't only about syntax, it's also about
               | the presence or absence of static types.
        
               | jakobnissen wrote:
               | Those two are related, clearly. It's not that Rust has
               | added words that literally do nothing. Instead, the
               | designers of Rust packed all kinds of things into the
               | syntax that other languages decided would cause too much
               | noise.                 * The output type must be
               | explicitly named instead of inferred       * The let
               | keyword for creation of variables instead of merely
               | assignment       * The mut keyword to distinguish between
               | mutable/immutable variables       * The pub keyword,
               | instead of another mechanism       * Semicolons needed
               | after every expression
               | 
               | And more. Again, for every one of them, you can argue
               | that it's _good_ for it to be there, because it serves a
               | function. But you end up with a language with a hugely
               | bloated syntax where the business logic is sort of
               | swamped in these accessory keywords.
        
               | Zababa wrote:
               | I don't agree that the syntax is "hugely bloated". The
               | return type is part of the business logic. mut and pub
               | are here to make some things explicit, so again I don't
               | mind them. Rust is one of those languages that wants to
               | make more things explicit because the people using it
               | need those things to be explicit.
        
               | tialaramex wrote:
               | > The output type must be explicitly named instead of
               | inferred
               | 
               | That isn't syntax. It's a decision about analysis.
               | 
               | > The pub keyword, instead of another mechanism
               | 
               | The "other mechanisms" discussed are in fact not
               | providing this feature, but pretending you needn't care,
               | and I'd argue Rust shows that in fact you should care and
               | pretending otherwise is a problem.
               | 
               | > Semicolons needed after every expression
               | 
               | On the contrary, semicolons are at the end of Rust's
               | _statements_. If what you 've got is an _expression_ you
               | don 't need a semicolon. For example here's how log is
               | defined on an f64 (a double precision floating point
               | number)                   pub fn log(self, base: f64) ->
               | f64 {             self.ln() / base.ln()         }
               | 
               | Notice there's no semicolon there because that function
               | is an expression, we want the _value_ of this division as
               | the result of the function.
        
           | scrubs wrote:
           | Agree.
        
         | fmakunbound wrote:
         | Regular jack-off here. It's hard enough coding in Rust as-is.
         | In addition to that, I don't think I could take into
         | consideration a coding style just to bring the build times
         | down.
        
       | thinkafterbef wrote:
       | Awesome work!
       | 
       | If you want to cut the CI run time even further. I'd recommend
       | BuildJet for Github Action. We give you managed high-performance
       | CI runners, which cuts both your CI time and price in half.
       | 
       | We plug right into Github Actions, so it's just one line change
       | in your yaml file.
       | 
       | https://buildjet.com/for-github-actions
        
       | smoldesu wrote:
       | I'm not an expert on the language, but I see Rust's build times
       | as fairly justified. Rust's focus is on execution speed and
       | correctness, which means that the compiler needs to be
       | _extremely_ pedantic with CPU targets to know how to optimize
       | your release. Once you have a target outlined, it has to acquire
       | and build the latest versions of your dependencies with the
       | highest level of optimization possible for your specific
       | architecture... that 's just a lot of computing, period. Caching
       | is a half-solution to this as the author suggests, but frankly
       | there isn't much juice left to squeeze out of the Rust fruit.
       | 
       | Now, there are still ways to make the compiler choke: the mention
       | of instantiations towards the end is a great example. But in my
       | (non-professional) experience, the build times are pretty
       | tolerable for what it's providing.
        
         | Kenji wrote:
         | People who complain that Rust build times are too slow are
         | python and webshit cucks who have no clue about what it means
         | to compile a million lines project for a particular
         | architecture. They never compiled C++ projects and they have no
         | clue that C++ compile times are comparable to if not slower
         | than Rust compile times. Long story short: People who complain
         | about Rust build times are clueless morons.
        
         | ReactiveJelly wrote:
         | The critics are right that the build times are slow even in
         | debug mode, and that it's one of the most frustrating things
         | about the language even for experienced users.
         | 
         | There are some alternate codegen backends that aim to fix this
         | by compiling less-optimized debug builds more quickly.
        
           | sodality2 wrote:
           | Ooh, got a link to any of those alternate backends?
        
             | pjmlp wrote:
             | For example Cranelift and using lld as linker.
             | 
             | Also read the Bevy documentation about improving build
             | times.
        
         | rhn_mk1 wrote:
         | What is the reason to downvote this?
        
           | lostdog wrote:
           | I didn't downvote it, but it feels like the parent comment is
           | making excuses for rust build times being slow. As someone
           | who's worked with C++, slow build times are a leech to
           | productivity, and I expect new languages to have a better
           | solution than "whatever."
        
         | eminence32 wrote:
         | > the build times are pretty tolerable for what it's providing.
         | 
         | I think this is probably right, and I think just about everyone
         | "tolerates" the build times (that is, I see very few people
         | stop developing in rust because of the build times).
         | 
         | But when, for example, I run `rustup update` to get a new
         | nightly compiler and have to re-compile everything, it's
         | annoying to have to wait 20 minutes. I'll tolerate it, and if a
         | magic wand could make it instantaneous I would love that. But
         | it's still annoying.
        
           | tanaypingalkar wrote:
           | I have a low spec machine but i still patiently wait for 200
           | crates to download.Because it is cached. But the most
           | annoying thing is that when I am using any frameworks like
           | gui and game. The second compilation also takes a minute to
           | start the app. I have ended up with them. :(
        
       | dhosek wrote:
       | I'm old enough that I can remember having to wait 45 minutes for
       | a compile to finish and _not be able to do anything else with the
       | computer_. The worst cases of this were the slow processes which
       | required occasional manual intervention. Or, the situation where
       | if there was an error during compilation, only the first error
       | was reliably meaningful and everything after that might or might
       | not be an accurate reflection of the state of the source code.
       | 
       | It's worth noting that there's a sort of uncanny valley in the
       | amount of time something takes: when it's short, it's not too big
       | a deal to wait for it. When it's fairly long (and predictably
       | so), it's easy enough to context switch (I used to read a lot
       | waiting for compiles to finish). It's the stuff in the 5-10
       | minute range that kills you.
       | 
       | What depresses me, is that while I can now do something else in a
       | different window while a process runs, the issues with errors and
       | requiring interaction still persist.
        
       | justinsaccount wrote:
       | I tried again to use rust for something the other day. Just
       | wanted to fetch some things via http. Nothing fancy, didn't even
       | really care about the response body, just the timings.
       | 
       | Consensus seemed to be that reqwest is the way to do this using
       | rust, since the stdlib does not include an http client.
       | 
       | Adding the dependency on reqwest tried to pull in over 100
       | dependencies and after building for a while, it failed somewhere
       | along the way because my rust version was too old. Now, maybe
       | some of these were optional dependencies, but they all got pulled
       | in automatically.
       | 
       | so, when TFA says
       | 
       | > Another obvious advice is to use fewer, smaller dependencies.
       | 
       | I just went back to go, which can do the crazy obscure task of
       | making an http request without requiring 100 external
       | dependencies.
        
         | SquishyPanda23 wrote:
         | > Just wanted to fetch some things via http. Nothing fancy,
         | didn't even really care about the response body, just the
         | timings.
         | 
         | why not bash and curl?
        
           | justinsaccount wrote:
           | Because I wanted to do other things with the timing data.
        
             | tinco wrote:
             | Why would you do it in Rust if you can do it in Go? Was
             | there some realtime constraint in making the web request?
        
               | justinsaccount wrote:
               | I was prototyping something that could monitor a few api
               | endpoints in parallel, collect timing stats using sqlite,
               | then output a static status page, statistics and graphs.
               | 
               | Kind of like what smokeping does, just wanted to see what
               | a from-scratch implementation that did just what I needed
               | might look like.
               | 
               | Ended up being about 400 lines. Only dependencies were
               | go-sqlite and go-chart.
        
         | sodality2 wrote:
         | `ureq::get("url").call().unwrap().to_string().unwrap()` with a
         | stopwatch should get you where you want. Add ureq as a
         | dependency, of course, but I found ureq to be better than
         | reqwest with about half the dependencies.
         | 
         | Or... `rustup update`...
        
         | PragmaticPulp wrote:
         | Those dependencies aren't recompiled every time you change
         | something. They will only impact your initial build or a clean
         | build on a CI/CD system.
         | 
         | Raw dependency counts can be shocking for those coming from
         | other languages where pulling in a dependency is an uncommon
         | operation, but it's not a bad thing. I'd rather have my HTTP
         | client library re-use popular, tested packages for things like
         | decoding JSON, handling TLS, working with IP addresses, and
         | other common tasks. I don't think it would be better to have
         | every package roll their own versions of all of those common
         | pieces.
         | 
         | You can browse the dependencies of reqwest here:
         | https://crates.io/crates/reqwest/0.11.4/dependencies
         | 
         | Notably, several of the big dependencies are maintained by the
         | same person who maintains reqwest. It's a common practice with
         | Rust crates to split a project into reusable parts. Reqwest
         | uses hyper (http client), http, http-body, mime, url, and
         | several other crates that are owned by the person who owns the
         | reqwest crate.
         | 
         | If we instead mashed all of those dependencies together into a
         | single crate it would appease the people who don't like seeing
         | high dependency numbers, but it wouldn't really make the
         | product any better. It would make it harder to re-use the
         | individual pieces, though.
         | 
         | > I just went back to go, which can do the crazy obscure task
         | of making an http request without requiring 100 external
         | dependencies.
         | 
         | Go is famous for including HTTP utilities in the standard
         | library. If I was building a simple app that made HTTP requests
         | and little more, I'd probably pick Go as well. Or Python.
         | 
         | Or you could have searched for a minimal HTTP request crate (
         | https://github.com/algesten/ureq ) designed to be small instead
         | of using the most full-featured crate.
         | 
         | Or you could have searched for Rust libcurl bindings (
         | https://github.com/alexcrichton/curl-rust ) to use an old
         | standby.
         | 
         | If your goal is to reach for something quick and easy with
         | batteries included and minimal external dependencies, Rust is
         | not a good choice. Doesn't mean Rust is "bad", but it's not the
         | right tool for every simple job. You also can't just pick the
         | most popular crate and assume it's exactly what you want.
        
           | smoldesu wrote:
           | > It's a common practice with Rust crates to split a project
           | into reusable parts.
           | 
           | Precisely this. Rust is not the same as Go or Java or Python:
           | it's still a fairly low-level language. Standard practice is
           | to build lower-level components (like http parsing and url
           | crates) that are abstracted over by more 'friendly' crates
           | (like reqwest). It's jarring, and certainly different from a
           | lot of other languages, but it's a testament to the
           | language's flexibility in my eyes.
        
         | [deleted]
        
         | ReactiveJelly wrote:
         | Yeah Go just has those as internal dependencies in the stdlib.
         | 
         | That _is_ the tradeoff - Rust builds up from nothing and makes
         | it friction-less to add a new dependency. Go starts at a higher
         | level and the friction for adding new stuff is high. And some
         | of the high-level stuff, like the GC, can't be removed.
        
           | lumost wrote:
           | Rusts ability to squeeze into small footprints is a huge part
           | of the appeal. I don't think that that excuses the
           | exceptional difficulty of writing a web server or client in
           | rust relative to other languages.
           | 
           | How often are folks writing applications that _don't_ need to
           | connect to the web these days?
        
             | kibwen wrote:
             | _> How often are folks writing applications that don't need
             | to connect to the web these days? _
             | 
             | For Go, perhaps rarely. For Rust, not needing to touch the
             | web is extremely common.
             | 
             | Let's also keep in mind that Rust's stdlib does include IP,
             | TCP, and UDP.
        
             | msbarnett wrote:
             | > How often are folks writing applications that don't need
             | to connect to the web these days?
             | 
             | In Go? Rarely, I would expect. In Rust, I'd assume
             | connecting to the web is the exception rather than the
             | norm. Most Rust projects tend to be lower-level
             | infrastructure & systems programming.
             | 
             | This probably explains, and in turn is explained by, their
             | different choices re: http in stdlib. They have much
             | different aims and target use-case.
        
             | at_compile_time wrote:
             | I just tested this. It took me 43 second to build a new
             | project with reqwest as a dependency. The second build took
             | 0.55 seconds. If that's exceptional difficulty, I'm
             | underpaid.
             | 
             | Maybe the lesson here is to update Rust if you haven't used
             | it in a while?
        
             | wongarsu wrote:
             | I didn't find writing a simple web server or client in Rust
             | any harder or simpler than in e.g. Python. What is this
             | exceptional difficulty we are talking about?
        
             | Ar-Curunir wrote:
             | It depends on your niche; as a person writing cryptography
             | libraries in Rust, I have never needed to make a HTTP
             | request.
        
           | mseepgood wrote:
           | > and the friction for adding new stuff is high
           | 
           | Not really, it's just a 'go get'.
        
         | mamcx wrote:
         | I just wanna say why is this in Rust:
         | 
         | Rust is a _system language_.
         | 
         | For example, one shock is that you can't print! or dbg!
         | anything (without adding Debug/Display to each type).
         | 
         | The reasons?
         | 
         | Where it will print in a Airpod? yeah: Rust is made to work in
         | places where NOT exist a place to print to. Or where NOT exist
         | a filesystem, or HTTP because not exist TCP because not exist a
         | network card.
         | 
         | So, in Rust all start in the deepest of the deepest bottom.
         | 
         | ---
         | 
         | I have a good metric to select frameworks/libraries:
         | 
         | - If is too complex, NO except if is the only game on town
         | 
         | - If have crazy deps setup (ejem: OpenSSL, bastard mess of
         | dep!) NOOOOO. (except if is the only game on town)
         | 
         | - If is too big, damm be good and solid (ej: PostgreSQL)
         | 
         | Whatever the lang, this have served me well, and when I have
         | been lazy (I get a deps to OpenSSL and waste 1 week fixing CI
         | builds with it then remember this and cut that mess) the pain
         | quickly fix it.
         | 
         | So, next time you get a trouble like this (in Rust or whatever)
         | ask what alternatives you can use instead!
        
         | xcambar wrote:
         | Funny thing is, Rust and JS are both given that comment.
         | 
         | Seeing how they are different languages, there's not a single
         | language feature that explains this dependency gluttony.
         | 
         | I'd like to open up the option that it is a necessary side-
         | effect of large ecosystems: more devs using the language, more
         | packages published, more quality in small packages. And then,
         | when building a large-ish package, it only makes sense to use
         | existing packages rather than reinventing the wheel.
         | 
         | And so, driven by nothing but good will and best practices,
         | since you have a lot of prebuilt "assets" at your disposal
         | ,your project ends up with 100s of deps and everybody
         | complains.
        
           | codefined wrote:
           | Rust and JS both have a very small standard library. This is
           | what unites them in a 'dependency hell' of having to handle
           | thousands of packages for most projects.
           | 
           | Other languages that don't face this, e.g. Python, have very
           | large standard libraries which already include a dizzying
           | array of features. E.g. take a look at some of the things you
           | get with standard Python[0].
           | 
           | - sqlite3 (DB-API 2.0 interface for SQLite databases)
           | 
           | - bz2 (Support for bzip2 compression)
           | 
           | - sunau (Read and write Sun AU files)
           | 
           | - netrc (netrc file processing)
           | 
           | - curses (terminal handling for character-cell displays)
           | 
           | - mailbox (manipulate mailboxes in various formats)
           | 
           | [0] https://docs.python.org/3/library/
        
             | mrec wrote:
             | There's a downside to Python's batteries-included approach
             | though:
             | 
             | https://leancrew.com/all-this/2012/04/where-modules-go-to-
             | di...
             | 
             | HN discussion: https://news.ycombinator.com/item?id=3913182
             | 
             | The obvious compromise would be to let libraries bake as
             | third-party projects for a good long while until there's a
             | fair amount of consensus that they're the Right Thing To
             | Do, _then_ pull them into the standard library. Java 's
             | Joda-Time would be one example.
        
               | smoldesu wrote:
               | Oracle doesn't own Rust though, there's no real push to
               | onboard these features besides the general curb-cutting
               | efforts for newcomers. For the most part, 'integral'
               | crates like serde and tokio have been community-managed
               | without issue so far, so it doesn't make much sense to
               | pull one open-source developers passion project from them
               | and pass it to another.
        
           | _tom_ wrote:
           | I don't think this is a characteristic of languages or a
           | large ecosystems. It's a side effect of dependency managers
           | making it seem like zero cost to include another library.
           | Including one dependency can end up pulling in an entire
           | ecosystem of connected dependencies, when the perhaps one
           | function in the original dependency.
           | 
           | One recent build I did, a small library included a dependency
           | to build its documentation. This was a full-fledged
           | documentation project, which pulled in hundreds more
           | dependencies.
           | 
           | It would be interesting to see a display of "cost" in
           | projects indicating how much additional dead weight each
           | individual dependency pulls in. This might lead to slight
           | better decisions on dependencies to choose.
        
             | mrec wrote:
             | > _It would be interesting to see a display of "cost" in
             | projects_
             | 
             | cargo-bloat is kind of along these lines, although it only
             | measures the absolute size of each dependency (as opposed
             | to "dead weight") and has some platform limitations.
             | 
             | https://crates.io/crates/cargo-bloat/
        
           | jollybean wrote:
           | " it is a necessary side-effect of large ecosystems"
           | 
           | It depends a lot.
           | 
           | Java has a huge ecosystem, but almost all of the core
           | functionality is provided for you.
           | 
           | Like everything there's a 90/10 rule in that 90% of the
           | 'things we need' really amount to only 10% of the total
           | packages out there, so it actually makes sense that the
           | people backing the platform provide for it.
           | 
           | Language, compiler, debugger, documentation, tutorials,
           | standard libraries etc. - it's all part of the platform.
           | 
           | Looking at it that way, it becomes a little more obvious why
           | even some cool languages don't quite breakthrough.
        
             | xcambar wrote:
             | Java is a good example.
             | 
             | As another commenter pointed out, it may relate more to the
             | size of the standard library than to the size of the
             | ecosystem. Which your comment seems to emphasize as well.
        
           | ReactiveJelly wrote:
           | > when building a large-ish package, it only makes sense to
           | use existing packages rather than reinventing the wheel.
           | 
           | Yes, for instance one of `reqwest`'s dependencies is
           | `base64`. We can argue both that re-inventing base64 in every
           | big library is pointless, and also that a language doesn't
           | need base64 in its stdlib because C and C++ don't have it.
           | 
           | There's no right way to cut this cake - Many a language has
           | included something in the standard library, and then
           | struggled to fix a broken interface once everything depended
           | on it. (Like JS picking UTF-16 for strings) Many a language
           | has also failed to include something in the stdlib and caused
           | every application to suffer and re-invent it. (Like C++ not
           | really saying anything about Unicode strings, which means Qt
           | and everyone else has their own little take on it.)
           | 
           | And the suffering is only bigger if you make dependencies
           | artificially hard to add.
           | 
           | Rust's approach is more flexible, at the cost of building the
           | universe from scratch every time you make a new workspace.
           | Personally I don't mind it. At least it's not like the
           | problems I've heard from Haskell land. Moving from futures to
           | async in Rust was fairly smooth.
        
             | tptacek wrote:
             | Rust should include Base64 in the standard library, and
             | languages should continue to standardize on UTF-8.
             | 
             | Doesn't seem hard to work out.
        
               | kibwen wrote:
               | The process for proposing new modules for Rust's stdlib
               | is pretty streamlined. Small API additions can be
               | proposed directly via PR to rust-lang/rust, whereas large
               | API additions can be brought up for discussion in rust-
               | lang/rfcs. If anybody thinks that base64 deserves to be
               | in Rust's stdlib, I encourage them to propose it (to my
               | knowledge nobody ever has).
        
               | burntsushi wrote:
               | You're nitpicking one example. Putting base64 into std
               | makes the higher level comment go from complaining about
               | 100 dependencies to complaining about 99 dependencies.
               | 
               | So maybe it's harder to work out than you think.
        
               | tptacek wrote:
               | I don't have an opinion about the broader dependency
               | problem in Rust. The comment to which I responded was
               | hosting an internal debate about Base64 and UTF-8. I
               | think that debate is easy to resolve: use UTF-8, put
               | Base64 in the stdlib. Do you disagree?
        
               | burntsushi wrote:
               | The comment you responded to was using base64 as an
               | _example_. That 's why your comment is a nitpick in this
               | conversation.
               | 
               | And yeah, I doubt I would support including base64 in std
               | unless there were a compelling reason to do so. I can't
               | think of one.
               | 
               | And yes, use UTF-8. Not contesting that.
        
               | tptacek wrote:
               | I guess I'd just say that the aggregation of these "keep
               | base64 out of std" decisions are why every Rust thingy I
               | put together ends up with a zillion dependencies. There
               | might be an upside to that (I have not detected it), but
               | there's an obvious downside.
               | 
               | Rust gets a bunch of stuff right that other languages
               | missed. I don't think this one of them. I think Go's
               | strategy of having a pretty-ok version of most of the
               | table-stakes things developers do is probably the right
               | one, just like I think sum types and option/result are
               | better than error types and `nil`.
               | 
               | I share your irritation at people who drop into the
               | middle of discussions like these to nitpick random
               | language war arguments, but this is a thread that is
               | actually about Rust dependency hell, and the comment I
               | responded to, I think, sort of diagnoses why Rust has
               | that "problem" (if you think it's a problem --- I do, but
               | whatever).
               | 
               | For whatever this is worth, by the way, languages that
               | have Base64 in std appear to include Ruby, Javascript,
               | Python, Clojure, Java, Scala, Go, Elixir, Nim, and Swift.
               | OCaml, C++ and Haskell do not.
        
               | pixel_fcker wrote:
               | What's the "obvious" downside?
        
               | tptacek wrote:
               | Having to find, download, review, and build a bunch of
               | extra dependencies (and surveil all the dependencies you
               | _do_ explicitly want to make sure they 're not pulling in
               | fucky code to do table-stakes things that the stdlib
               | could just stdize.)
               | 
               | Again: these are _downsides_ , but not _dispositions_. On
               | balance there could be good reasons to have a terse,
               | svelte, balletic std. I 'm just saying, there's clearly a
               | downside to the decision.
        
             | awsthro00945 wrote:
             | >and also that a language doesn't need base64 in its stdlib
             | because C and C++ don't have it
             | 
             | I don't follow this logic. If we just wanted to do
             | everything the way C/C++ does it, we'd be using C/C++, and
             | Rust wouldn't exist.
        
               | pjmlp wrote:
               | True, although my only complain with C++ is the
               | subculture that to this day pushes for C types and unsafe
               | by default.
               | 
               | For anything else I don't have any major complaint.
        
               | secondcoming wrote:
               | who is pushing for 'unsafe by default'?
        
               | pjmlp wrote:
               | Anyone that uses char * instead of std::array for
               | example.
        
               | yakubin wrote:
               | _std::array_ will instantiate function templates for all
               | used array sizes, lengthen compilation time, and be hard
               | to use across separately-compiled code without putting
               | code into headers. It has its uses, but more often I 'd
               | prefer _T[N]_ with bounds-checking, and, in another
               | world, a builtin variably-sized, non-resizable array
               | type; basically a sort of _absl::FixedArray_ [1], but
               | with the semantics of _operator[]_ and _at()_ reversed,
               | so that bounds-checking is the default. Sadly, nothing
               | like that is included in the standard, so won 't be a
               | lingua-franca.
               | 
               | [1]: <https://github.com/abseil/abseil-
               | cpp/blob/master/absl/contai...>
        
               | tialaramex wrote:
               | The C++ Standards Committee itself.
               | 
               | Take expected (which missed C++ 20 but may land in C++
               | 23). The idea here is like Rust's Result type+, but C++
               | lacks a sum type so it's instead presented a bit like a
               | smart pointer. You check it for errors and then just
               | dereference expected to get your answer if there weren't
               | any errors. Simple. If you can't be bothered to do error-
               | checking, no worries, dereferencing expected when it's an
               | error throws an exception, and your usual C++ exception
               | handling applies.
               | 
               | But no, the committee says this doesn't look dangerous
               | enough to us, so they _required_ it to be rewritten to
               | say dereferencing expected when it 's an error is
               | Undefined Behaviour instead of throwing an exception.
               | 
               | Imagine deliberately adding more Undefined Behaviour to a
               | programming language (technically in this case the
               | language's standard library) in response to work to
               | compete with safer languages. It's an act of self-
               | sabotage without question.
               | 
               | + Unlike exceptions, in Result your error is just more
               | data, to examine, process, store and use now or later or
               | not at all at your option. An exception changes control
               | flow in the program when the error occurs, which may not
               | be what you need at all. This is crucial to some designs.
        
               | pjmlp wrote:
               | Yeah, the "there should not exist any language underneath
               | besides Assembly" motto pushed to extreme, is what might
               | eventually sink all efforts to make C++ safer alternative
               | than plain old C.
        
               | tialaramex wrote:
               | I don't see this part though. "Leave no room for a lower
               | level language" doesn't require you to go around defining
               | every mistake as Undefined Behaviour, that's crazy. A
               | different part of the committee managed to see in 2021
               | that if your format can be statically detected as bogus
               | (e.g. you tell the formatter you're giving it a string,
               | then hand over a floating point type variable) then the
               | compiler should be obliged to emit a diagnostic and give
               | up compiling the program, not hand you a program that it
               | knows can't work and enjoy your humiliation when it fails
               | in production. _That 's_ the sort of safety improvement
               | you'd want to see.
               | 
               | As I understand it the runtime performance-at-all-costs
               | school, which includes people from Google, don't want the
               | C++ language to be deliberately _unsafe_ they just don 't
               | prioritize safety over performance. This group won't
               | allow C++ to have runtime bounds checking for the array
               | subscript operator or its overloaded equivalents and _I_
               | think they 're wrong about that, but it's not making the
               | problem _worse_. Stuff like ranges, views and iterators
               | (even though C++ iterators are incredibly ugly) mean in
               | idiomatic C++ today you 're rarely reaching for this
               | particular gun that's supplied loaded and pointing
               | directly at your feet. In contrast what was done to
               | expected just seems like self-sabotage. If it's too
               | _slow_ then don 't use it. Or rather, as these same
               | people would be the first to point out, _measure_ to
               | check your intuition that it 's too slow and only then if
               | it really is don't use it.
        
               | pjmlp wrote:
               | That is exactly what I mean, the leave no language
               | underneath agenda is being pushed by the performance at
               | all costs crowd.
               | 
               | In all these years I have left RTTI enabled, used
               | exceptions, tried to keep bounds checking enabled in STL
               | types, and it was never an issue in production.
        
             | hardwaregeek wrote:
             | Yeah in the debate about dependencies, people often frame
             | it as more dependencies == bad and less dependencies ==
             | good. Except more/less dependencies is actually more or
             | less _explicit_ dependencies. Fact of the matter is that
             | you need a crazy amount of dependencies to write most
             | modern software. You need ways to handle Unicode, to
             | connect to databases, to write async code, to handle dates,
             | etc.
             | 
             | The question is whether that code is going to come from a
             | monolithic Swiss Army Knife of a library (aka a standard
             | library), a bunch of packages, or some code that everybody
             | copies into their codebase. In which case the packages
             | option doesn't seem so bad. At least they're explicitly
             | versioned, tracked for vulnerabilities and can be updated
             | at a granular level.
        
           | mirekrusin wrote:
           | True in js/ts world this is a problem. Since couple of years
           | I've been experimenting with more radical fat trimming on
           | dependencies. It works very well on backend code. I belive
           | shallow or no dependencies is the way to go.
           | 
           | What seems to be missing is standard library as constellation
           | of well designed modules/packages. I decided to just do it
           | for myself (and anybody who finds it interesting). I don't
           | think it's going to fly high but if somebody is interested
           | the link is here [0]. I'm adding modules every few days.
           | 
           | The aim is to have something worthwile to show around the
           | time node v16 goes lts. I have some experience taking care of
           | high stake business critical systems. This is fun side
           | project as a hobby atm. Modules being dropped there are
           | reflection of problems I'm facing during my day-to-day work.
           | 
           | You'll see there some non conventional code. Large parts are
           | inspired by functional programming, ocalm and simplicitly in
           | general. Personally I find it exciting how well this kind of
           | approach fits into production projects.
           | 
           | The nice thing about this approach is that you can have niche
           | leaf packages/modules as part of standard library
           | (constellation) - if you don't want to use it, it doesn't
           | matter, it wont incur any cost. For example order book module
           | or exchange matching engine - those can simply live there, be
           | useful to people who care and have zero impact to people who
           | don't care at all. I find it to be an interesting difference
           | from builtin, shipped library.
           | 
           | Large package ecosystem is a sign of popularity. With time
           | libraries should converge onto shared approaches.
           | Interestingly practice shows wide spread of duplication. I
           | think more effort from big players with wide reach could
           | reduce it. Ie. not me doing stuff but Microsoft or others
           | picking up the ball.
           | 
           | [0] https://github.com/preludejs
        
         | [deleted]
        
         | batterylow wrote:
         | Try ureq instead, like in this article:
         | https://datacrayon.com/posts/programming/rust-notebooks/type...
        
         | duped wrote:
         | You could just use Curl bindings like you would in C or C++,
         | which is a better comparison than Go.
        
         | serverholic wrote:
         | So you gave up because your rust version was too old?
         | 
         | Dude it's like one command to update.
        
           | GrayShade wrote:
           | Not if you're using the Debian version.
           | 
           | They could at least package rustup, I have no idea why they
           | don't.
        
             | pornel wrote:
             | Debian's Rust is useless, sorry.
             | 
             | Rust went for "evergreen" approach like Chrome. It focuses
             | on making small gradual changes, and quick and painless
             | upgrades. This allows everyone to only target one version:
             | the current stable one. Debian's philosophy is the polar
             | opposite, so Debian's Rust version is already obsolete even
             | on the day of a new Debian release.
        
       | carlmr wrote:
       | >[Build time] also is a non-linear factor. Just waiting for the
       | compiler is the smaller problem. The big one is losing the state
       | of the flow or (worse) mental context switch to do something else
       | while the code is compiling. One minute of work for the compiler
       | wastes more than one minute of work for the human.
       | 
       | This is so true, and so hard to convey to a lot of people, even
       | in engineering. They like to abstract humans as machines that can
       | just context switch at a whim, which is just not true.
        
       | pjmlp wrote:
       | The irony of cargo not supporting binary libraries is that the
       | usual solution ends up reducing the number of dependencies, and
       | being forced to rewrite ourselves the solution in another way.
        
         | kibwen wrote:
         | If you want to set up something like this for your own use,
         | look into sccache. Integrating this more broadly into cargo
         | would be generally pretty useless without a stable Rust ABI.
        
           | pjmlp wrote:
           | Yeah that is a possible solution, but still not quite what
           | vcpkg, conan and distribution repositories offer, or having
           | COM/Windows Runtime libraries.
           | 
           | I look forward when cargo offers that as an option, doesn't
           | mean everyone has to offer their libraries that way, just
           | having the possibility on crates.io would be nice.
           | 
           | I am also very thankful for what has been achieved thus far,
           | so don't take this as thankless complaint rather as I would
           | like Rust one day to be.
        
       | jgavris wrote:
       | I'm surprised nobody has mentioned trying out using Google's
       | Bazel build system with the Rust rules. Bazel provides relatively
       | straightforward 'remote caching' of build artifacts via Google
       | Cloud Storage or S3.
       | 
       | https://github.com/bazelbuild/rules_rust
        
         | gravypod wrote:
         | Another few benefits with Bazel is that your build is mostly
         | hermetic and reproducible. I've never had to run `bazel clean`
         | and the output sha of your builds will always be the same (good
         | for signing and security). You can also use remote build
         | execution (RBE) to run your builds in parallel. This makes for
         | a massive performance boost when you're compiling a lot of
         | libraries (packages, source files, etc).
         | 
         | It also models your source code in your build system (exposing
         | libraries, binaries, etc).
         | 
         | I'd love to see a performance comparison with `rules_rust`.
        
           | jgavris wrote:
           | Of course, making the right abstractions and library / module
           | / crate boundaries is still very much important to
           | incremental build performance. For 'very large' Rust
           | projects, setting up remote build execution can be hard, but
           | a few of the original Bazel folks at Google are trying to
           | make that easier for folks with EngFlow
           | https://www.engflow.com/
        
             | gravypod wrote:
             | This space is pretty healthy right now! For an updated list
             | of people in this space you can also check out:
             | https://bazel.build/remote-execution-services.html
        
             | finnb wrote:
             | Engflow has some very smart people.
             | 
             | There are also a number of other open source alternatives
             | such as Buildbarn (https://github.com/buildbarn) and
             | Buildfarm (https://github.com/bazelbuild/bazel-buildfarm)
             | which you can host yourself.
             | 
             | Personally, I've found Buildbarn great to throw up in
             | Kubernetes and build massive projects with.
        
       | eminence32 wrote:
       | > Another obvious advice is to use fewer, smaller dependencies.
       | 
       | As a library author, I sometimes wonder if I should be
       | introducing more feature flags into my crate to let people only
       | use the bits that they want, in order to help compile times. The
       | whole thing doesn't take very long to compile (~20 seconds), but
       | maybe the added complexity of juggling feature flags might be
       | worth it.
        
         | stormbrew wrote:
         | As someone who works on a large rust codebase with a lot of
         | moving parts, I'd ask you to really think hard about how your
         | use of features might affect people down the line in
         | undesireable ways if you go down this path.
         | 
         | In particular, please for the love of god don't put the whole
         | kitchen sink into the default feature. I'd even argue don't
         | ever have a default feature at all. In practice, once a
         | codebase gets above a specific size, it becomes completely
         | impossible to exclude any features in a crate's default list
         | because `default-features=false` only applies locally.
         | Something 5 levels deep will bring those features in whether I
         | like it or not just by not specifying anything.
         | 
         | Also, please don't do things like how slog uses a feature flag
         | to control minimum log levels, or k8s-openapi uses it for
         | version selection. These kinds of usages create extremely
         | frustrating problems.
         | 
         | Honestly, features -- and especially the way default features
         | work -- are the worst thing about cargo to me. I think they
         | should be a lot more restricted than they currently are. If I
         | had my way you'd only be able to use `#[cfg(feature = X)]` at
         | module level to include or exclude code, never to select actual
         | behaviour.
         | 
         | To me they're filling a hole that crates themselves should be
         | filling. Certain language features aren't really rich enough to
         | make really effective 'additive' crates though, unfortunately.
        
           | matklad wrote:
           | > In practice, once a codebase gets above a specific size, it
           | becomes completely impossible to exclude any features in a
           | crate's default list because `default-features=false` only
           | applies locally
           | 
           | Curious, what that specific size approximately is? For rust-
           | analyzer, getting all the deps to use features correctly is
           | rather annoying, but isn't particularly time-consuming.
        
         | dochtman wrote:
         | I only use feature flags for features that require an
         | additional dependency, or for features that really require a
         | whole lot of extra code.
        
         | mplanchard wrote:
         | Fwiw unless the flags really represent independent bits of
         | functionality, I find many features more of a pain to deal with
         | than dependency compile times, given that even in our big
         | workspace, a combination of incremental builds and sccache
         | makes compile times mostly a non issue.
        
           | eminence32 wrote:
           | Yeah, the position you articulated is my main concern. Do you
           | think it makes any differences if the features are enabled-
           | by-default? If so, the "default" experience isn't any
           | different than it is today, but discerning consumers can
           | disable things if they need it. But also, an "everything-by-
           | default" approach doesn't seem that helpful to the global
           | ecosystem.
        
         | jeroenhd wrote:
         | I'm just one voice (and I'm probably not someone who uses your
         | specific library), but this does sound like a good idea. I'd
         | appreciate it (though I understand that this takes effort and
         | maintenance, so I understand if you don't want to!)
         | 
         | Your 20 second library is not too bad when it's used on its
         | own, but add 5 or 6 libraries that all "only" take a few
         | seconds and you're waiting a solid five minutes compiling other
         | people's code and their dependencies.
         | 
         | Even simple Rust programs quickly end up compiling several
         | versions of several libraries because of the dependency tree,
         | some of which can include large libraries like Tokio and Serde.
         | I love libraries that make use of feature flags, they massively
         | cut down on my compile time.
         | 
         | This isn't really any library developers' fault, it's a problem
         | with the entire ecosystem. Cargo is not dissimilar from NPM
         | where you can quickly end up with an exploded dependency chain
         | of hundreds of megabytes in supporting code before you even
         | realize.
        
         | edflsafoiewq wrote:
         | Doesn't this basically require buy-in across the entire crate
         | tree? Eg even if I disable all the default features I don't
         | need, all it takes is a single crate anywhere in the dep tree
         | that uses the same crate with the default features to turn them
         | on again?
        
         | nicoburns wrote:
         | You think 20 seconds isn't a long compile time!?
        
           | [deleted]
        
           | axiosgunnar wrote:
           | I don't see the problem. Since it's an external crate, it
           | will compile exactly once after adding it to Cargo.toml, and
           | will be cached forever.
        
             | finnb wrote:
             | Cached locally, sure. The point of bazel with remote
             | execution and remote caching is that it's then available in
             | the cloud for everyone to access.
        
             | unanswered wrote:
             | I'm convinced that most of the people complaining about
             | build times are using docker for some incomprehensible
             | reason and are blowing away the dependency cache with every
             | build.
        
               | rowanG077 wrote:
               | This. It's annoying to compile from scratch. But I almost
               | never have to do that. So I don't really have a problem
               | with compile times.
        
             | kzrdude wrote:
             | forever, or until rust version update (might be often for
             | nightly builds), or until build flag change or cargo clean,
             | whichever happens first. It's also only a per-project
             | target directory (not across all projects you compile).
        
               | kibwen wrote:
               | Even for people on nightly builds of the compiler, I
               | would be quite surprised if nearly anyone actually
               | bothered to upgrade their compiler every night. Most
               | people on nightly are just going to be there for the use
               | of a specific unstable feature.
        
           | alkonaut wrote:
           | It all depends on how many times you do it! 20 seconds for an
           | edit-compile loop is huge. 20 seconds for a CI build is not
           | as bad, but still bad (if you have dozens more).
           | 
           | 20 seconds once per machine per dependency version (i.e.
           | there is no rebuild of the dependency until you change
           | compiler flags or update the library) is OK.
        
           | jeroenhd wrote:
           | This depends on what the library does, of course. From a
           | quick Google I believe the parent has made several packages,
           | like a front-end to help with XML trees or a full wrapper
           | around procfs. Libraries that complex (especially with their
           | dependencies) can take significant time to compile, it's just
           | a consequence of their complexity.
           | 
           | Some smaller libraries I've seen that just do some annoying-
           | to-test but simple string manipulations have had similar
           | compile times, mostly because they include specific versions
           | of advanced logging or error reporting libraries for no
           | obvious reason.
        
             | eminence32 wrote:
             | Yup, that's me :)
             | 
             | For example in the `procfs` crate, there's a dependency on
             | `flate2` just in case a /boot/config.gz file exists (in
             | order to see what options the kernel was built with). But
             | if a consumer never needs to inspect the kernel options,
             | this dependency (and related code) is just dead-weight.
             | 
             | The nice thing about rust is that this code will be removed
             | from the final executable if it's unused, but maybe it's
             | time to start thinking more carefully about compile times.
        
       ___________________________________________________________________
       (page generated 2021-09-05 23:01 UTC)