I'd like to start a project, manually import libraries (downloaded manually, no dependency hell), read documentation, etc. Is it possible?
Yes. Once you have downloaded your dependencies the first time while online you are able to work with them completely offline.
> read documentation
Rust docs:
rustup downloads docs for Rust itself alongside the toolchain when you download it.
`rustup doc --book` will open the locally downloaded copy of the book The Rust Programming Language in your browser.
`rustup doc` will open the locally downloaded copy of the overview of Rust Documentation in your browser. The locally downloaded docs include things like the docs for the Rust Standard Library.
Project and dependencies docs:
`cargo doc --open' will build the docs for your project and for your dependencies as offline HTML files and open the locally build docs in your web browser for you.
Subsequently running the same command while offline will open the already built docs in your web browser again.
You will find the built docs under target/doc/ in your project. This includes the docs for your dependencies and their dependencies and so on.
And even if you delete the built docs, for example by running `cargo clean', cargo can rebuild the docs offline because it has cached the source code of your dependencies and their dependencies and so on.
> manually import libraries (downloaded manually, no dependency hell)
Rather than attempt to do it manually I would advice that you run `cargo build' once while online, so that your dependencies are fetched and made available offline. Attempting to do it completely manually has no benefit that I can see and would only serve to waste time and probably introduce problems that would not happen if you leave it to cargo to fetch it all for you.
And you can write crates of your own locally, never publish them online and import them by relative local path.
> Attempting to do it completely manually has no benefit that I can see
Package managers and build tools can do basically whatever they want - run commands, execute binaries, download data, upload data, send telemetry - whatever any one of the package maintainers wants.
I prefer to develop in an offline VM. When a new library is required I just download it using the host machine, copy into VM and use it there. It's easy with make/cMake.
You can also use rustc without cargo if you really want, but Cargo is really good, so I don’t particularly recommend it.
Then, if using cargo which I totally would, run ‘cargo doc —open’ to see api documentation for your project as well as your projects dependencies both direct and indirect.
All from disk!
Rust team can't do much about ecosystem though. There are some libraries which download resource (say, large data table) over the network at build time. I consider these bugs, but maintainers of those libraries may disagree.
The documentation for using rustc directly is hard to find, but it’s quite possible to skip Cargo entirely. I’ve had no trouble using it mostly like a C compiler, but I have run into a couple of quirks I needed to work around:
* I haven’t figured out how to get dependency information out of the compiler, so I had to write a likely-fragile script to grep the rust sources.
* In any project, crates have a single, flat namespace. If you want to treat them like you would object files in a C project you’ll need some scheme to prevent name conflicts between subdirectories. The more rust-y thing is to use the module system to include anything that shouldn’t be visible at the top level of your hierarchy.
Otherwise, it’s fairly similar to a traditional compiler:
rustc main.rs -o my-program # compile an executable
rustc main.rs --test -o ... # compile the test framework
rustc --crate-type=rlib ... # make an rlib (roughly equivalent to .o)
rustc --extern name=path/to/rlib ... # reference another crate (required during compilation)Also, you can run a local copy of all the documentation in all the repos pretty easily too.
Huge asterisk: I've never tested any phone-homes, if that is what you're worried about. But, it can be pretty offline-friendly.
if (0..=10).contains(&5) {
I assume we are taking a ref to "5" (and not passing by value) because "Range" is a generic type that might be too big to want to copy (or it may not be copyable at all). But taking a reference to a number for a simple operation like this feels... weird. It makes me worry that Rust is going to be passing around pointers to some stack-allocated "5", but I hope that Rust is actually much smarter than that?Instead, this is a borrow which indicates that the callee will not mutate the argument. It makes more sense in this context.
Isn't that also the official name of the feature in Rust? Isn't &T in Rust pronounced "reference to T" and "&mut T" pronounced "mutable reference to T"? That is the impression I get from: https://doc.rust-lang.org/book/ch04-02-references-and-borrow...
so yeah, it looks super weird, but when you stop to think about it, it's just that we are so used to integers being "special" that when we see them treated like a generic type it makes us cringe. interesting.
Edit: But in this case the reference will be optimized away anyway, because the callee function will be inlined.
Edit 2: Actually, the optimization does exist at the LLVM level, though it only applies in some cases. See my other comment:
Or is there something else going on?
This threw me for a minute. Function pointers not using * is confusing enough without using glob syntax to talk about function types!
Edit: the parent comment was replying to an earlier version of my comment, in which I used + because I didn't know that * could be kind-of escaped by putting a space after it
> Fn* closure traits implemented for Box<dyn Fn* >
This threw me for a minute. Function pointers not using * is confusing enough without using glob syntax to talk about function types!
Use four spaces at the
beginning of the line * *mut fn(T, ...) -> U1. How does this handle ranges whose length is larger than can be represented in an integer?
2. How does iterating a range work for floats? It looks like it just adds one [1], won't this mean that it will loop forever if the next representable float is +2?
1: https://doc.rust-lang.org/src/core/iter/range.rs.html#297
impl<A: Step> Iterator for ops::RangeInclusive<A> {
RangeInclusive<A> implements Iterator only for types A which implement Step. Floating point types like f64 are not Step:https://doc.rust-lang.org/std/iter/trait.Step.html
Thus RangeInclusive<f64> is perfectly valid, but RangeInclusive<f64> cannot be used as an Iterator.
See also the bounds on contains():
https://doc.rust-lang.org/std/ops/struct.RangeInclusive.html...
f64 is PartialOrd<f64>, so a RangeInclusive<f64> can be asked if it contains a specified f64. This definition would also allow one to ask a RangeInclusive<IpAddr> if it contains a specified IpAddr _or_ Ipv4Addr _or_ Ipv6Addr, since IpAddr is PartialOrd<IpAddr>, PartialOrd<Ipv4Addr>, and PartialOrd<Ipv6Addr>.
Any type A which can be compared to any other type B automatically gets RangeInclusive<A>::contains(B). Anything which doesn't can still be a RangeInclusive<A>, it just won't have contains().
2. The Iterator implementation for RangeInclusive requires the underlying type to implement Step [1], which floats do not. So you can't iterate over a range of floats (although you can still construct one).
[1] https://doc.rust-lang.org/nightly/std/iter/trait.Step.html
[1] https://doc.rust-lang.org/1.0.0/std/primitive.f64.html#metho...
[0] https://www.reddit.com/r/rust/comments/brtec1/rustup_1183_re...
https://doc.rust-lang.org/nightly/std/cell/struct.RefMut.htm...
[src/main.rs:4] x == 1 = false
[src/main.rs:9]
print this: [src/main.rs:4] dbg!( x == 1 ) = false
[src/main.rs:9] dbg!
Easier to filter just your debug messages.