Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> [...] My project has ~1700 lines of Rust, ends up pulling in ~700 dependencies [...]

I have no experience with Rust. Having 0.4 dependencies per line (so every 3 lines you are dependent on a 3rd-party) sounds very extreme. Can you elaborate a bit what those dependencies are (like many std libs?)?



You can see for yourself here: https://codeberg.org/ditzes/ditzes/src/branch/master/src-tau...

My project have ~30 dependencies defined for various features in the client. The rest are transitive.

Here is the output of cargo tree for your leisure: https://pastebin.com/aep1bX4v

A quick scan of cargo tree seems to indicate most of them comes from tauri and actix projects.

Edit: oh, and also tantivy (a search engine) ends up pulling in ~300 dependencies which is a pretty big chunk of the total


Something for people to keep in mind that this is pulling in the source for:

- An electron-like framework

- A web framework

- Multiple HTTP clients

- An HTML parser

- An image editor

- A search engine

Some potential dependency savings I noticed:

How come you pull in both isahc and reqwest? On the surface, don't they fill the same role? Similar for rustls and openssl.

If you upgrade your readability dependency, you will only pull in reqwest once instead of twice. Might be useful to run `cargo tree -d`.

I'm not familiar with savefile? What does that get you over serde_json which you also pull in?


Thanks a lot for taking a look at the dependencies! I'm gonna be honest and say that I haven't spent much time "optimizing" anything, I haven't even arrived at a "first" version yet with Ditzes. But with that said, you have some good points :)

> How come you pull in both isahc and reqwest? On the surface, don't they fill the same role? Similar for rustls and openssl.

Yes indeed. I started with using reqwest, but it's no longer used and only isahc is used, so reqwest can be removed from the list.

Same with openssl/rustls. Started with rustls, but not longer used, so can also be removed.

Simply forgot to remove them at one point I guess.

> If you upgrade your readability dependency, you will only pull in reqwest once instead of twice. Might be useful to run `cargo tree -d`.

Ah, that's good to know. I'll do that once I put in some other changes, thanks!

> I'm not familiar with savefile? What does that get you over serde_json which you also pull in?

Rust savefile persists structs as binary data on disk, and also have versioning. Started out persisting everything as JSON, but loading thousands of posts (or even hundreds) from disk and deserializing them from JSON turned out to be quite slow. So using savefile mainly for performance, but I like the versioning/migration aspect of it as well, although I haven't used it yet with Ditzes.

serde_json is mainly used to parse responses from the HN API and to output JSON from the HTTP API, if I recall correctly.


`cargo udeps` tries to uncover unused dependencies. I wish we had more reliable ways to detect it so we could integrate it into cargo.


That will be including transitive dependencies. Consider how many dependencies making a HTTP request will pull in. Ones for network IO, ones for parsing HTTP request. Possibly separate ones for HTTP1, HTTP2, HTTP3. Ones for type definitions of HTTP requests. Rust convention is to publish these bits as separate crates even if they are published by the same maintainer(s) so as to improve compile times (separate crates can be compiled in parallel) and modularity (if you're writing a competing library then you can share the internals of the existing implementation when possible).

This isn't like C++ where a single dependency is a huge project. Some crates are literally just trait (interface) definitions.


I compiled popular Rust projects before. It never pulled less than 300-400 dependencies. :/


Indeed. Here I took a random sample from https://github.com/topics/rust and listed the amount of dependencies from running `cargo tree` (full command: `cargo tree | awk 'NF' | wc -l`)

- denoland/deno (nodejs-like runtime) - 1550

- alacritty/alacritty (Terminal) - 303

- sharkdp/bat (`cat` clone) - 249

- meilisearch/meilisearch (search engine) - 1128

- starship/starship (command line prompt) - 427

- swc-project/swc (JS/TS compiler) - 1869

- AppFlowy-IO/AppFlowy (Notion clone) - 1146

- yewstack/yew (Rust/WASM web framework) - 942

- rustdesk/rustdesk (remote desktop) - 648

- nushell/nushell (shell) - 858

- ogham/exa (`ls` alternative) - 61

So yeah, seems even things as basic as a `cat` replacement ends up with 249 dependencies. Lowest amount of dependencies is a `ls` alternative, which ends up with 62 dependencies.


cargo tree | wc -l tends to over-count dependencies a lot because there are many very common crates.

Use grep package -F '[['package Cargo.lock | wc -l instead. This is doubly true if you pull in multiple crates using the same framework behind it, e.g. in async projects. E.g. for exa this is 45 instead of 61. For deno this turns into a 3x difference (515 vs. 1571).

Other reasons, besides what has been mentioned (vendoring being very uncommon, crates existing for common type and trait definitions, crates being generally scoped more narrowly resulting in more smaller crates), a lot of crates have additional crates for macros. Bindings often end up being a whole bunch of crates for this reason (i.e. one xyz-sys crate for the FFI bindings, plus one or more crates for a rusty wrapper and perhaps one or more crates for convenience crates). Case in point from deno's dependency tree:

    ├── futures v0.3.21
    │   ├── futures-channel v0.3.21
    │   │   ├── futures-core v0.3.21
    │   │   └── futures-sink v0.3.21
    │   ├── futures-core v0.3.21
    │   ├── futures-executor v0.3.21
    │   │   ├── futures-core v0.3.21
    │   │   ├── futures-task v0.3.21
    │   │   └── futures-util v0.3.21
    │   │       ├── futures-channel v0.3.21 (*)
    │   │       ├── futures-core v0.3.21
    │   │       ├── futures-io v0.3.21
    │   │       ├── futures-macro v0.3.21 (proc-macro)
    │   │       │   ├── proc-macro2 v1.0.38 (*)
    │   │       │   ├── quote v1.0.18 (*)
    │   │       │   └── syn v1.0.93 (*)
    │   │       ├── futures-sink v0.3.21
    │   │       ├── futures-task v0.3.21
    │   │       ├── memchr v2.5.0
    │   │       ├── pin-project-lite v0.2.9
    │   │       ├── pin-utils v0.1.0
    │   │       └── slab v0.4.6
    │   ├── futures-io v0.3.21
    │   ├── futures-sink v0.3.21
    │   ├── futures-task v0.3.21
    │   └── futures-util v0.3.21 (*)


Deps are a transitive tree. The number of deps for enduser code for a framework like this will always be high. That is kinda the point. A small amount of end user code uses the work of thousands.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: