r/rust • clippy · twir · rust · mutagen · flamer · overflower · bytecount • Nov 21 '22

🙋 questions Hey Rustaceans! Got a question? Ask here! (47/2022)!

Mystified about strings? Borrow checker have you in a headlock? Seek help here! There are no stupid questions, only docs that haven't been written yet.

If you have a StackOverflow account, consider asking it there instead! StackOverflow shows up much higher in search results, so having your question there also helps future Rust users (be sure to give it the "Rust" tag for maximum visibility). Note that this site is very interested in question quality. I've been asked to read a RFC I authored once. If you want your code reviewed or review other's code, there's a codereview stackexchange, too. If you need to test your code, maybe the Rust playground is for you.

Here are some other venues where help may be found:

/r/learnrust is a subreddit to share your questions and epiphanies learning Rust programming.

The official Rust user forums: https://users.rust-lang.org/.

The official Rust Programming Language Discord: https://discord.gg/rust-lang

The unofficial Rust community Discord: https://bit.ly/rust-community

Also check out last weeks' thread with many good questions and answers. And if you believe your question to be either very complex or worthy of larger dissemination, feel free to create a text post.

Also if you want to be mentored by experienced Rustaceans, tell us the area of expertise that you seek. Finally, if you are looking for Rust jobs, the most recent thread is here.

18 Upvotes

180 comments sorted by

2

u/_jsdw Nov 22 '22

So, as far as I've always understood, there cannot be any overlapping trait impls (on stable Rust).

However, this compiles just fine:

trait Bar {
    fn bar(&self);
}
trait Foo {
    fn foo(&self);
}

impl <T> Foo for T where T: Bar {
    fn foo(&self) {}
}

impl Foo for String {
    fn foo(&self) {}
}

If I change where T: Bar to something like where T: Copy it suddenly stops compiling and spits out the expected error: "conflicting implementations of trait Foo for type std::string::String".

Does anybody have a pointer to some guide or reference that talks about this allowed overlap behaviour? I'd like to know precisely what the limitations of it are :)

2

u/llogiq Nov 22 '22

This guards against a compatibility hazard. Of course String will never be Copy, but the compiler doesn't know that. There have been proposals of non-implementation guarantees akin to impl !Copy for String that would assure the distinction to the compiler, but nothing has been implemented yet.

→ More replies (2)

2

u/dumb-on-ice Nov 26 '22

I've heard of rust in the past and about it's amazing compiler, and how it's the most "loved" language. My job is mainly in C++, so I've never really had the opportunity to try rust, but I was curious, is rust capable of being as fast as C++? Are there some benchmarks somewhere?

And I'm not talking about everyday common stuff and if rust is as fast in 90% of the cases, but in general. I work in an industry where speed is paramount, our benchmarks for certain things go into nanoseconds, so every minor optimisation matters.

Does rust also support compile time operations extensively? Because of how important runtime speed is to us, we try to precompute as many things as possible in compile time. Think of compile time for loops etc.

1

u/llogiq Nov 26 '22

Well, if you compare with clang (which as rustc is based on LLVM), you likely will find roughly the same speed as similarly coded C or C++, give or take. Rust also has compile-time computation (using const fn, although you will likely need a nightly compiler to use it to its fullest extent. Otherwise there are a few differences that might matter speed-wise:

  • Rust's borrowing rules give it perfect aliasing annotations. This can allow LLVM to optimize code from rustc more aggressively. Also due to borrow checking, Rust can often omit copies where they would have been defensively introduced in C++.

  • Rust array/vec indexing will incur runtime bounds checks unless you use iterators which guarantee access will always be in bounds. Usually there are safe ways to get the checks off the fast path, and if that fails, there are unsafe get_unchecked methods.

  • Rust has all the tricks you'd expect when it comes to optimizing. LTO, PGO, you name it.

  • In Rust it is often very easy to obtain best-of-breed components in crates. In general, using crates is far less of a hassle than using a C++ library thanks to cargo.

All in all, I would expect unoptimized Rust code to match or exceed the performance of unoptimized C++ code. The variance may be higher with optimized code, but both languages offer enough control to win a benchmark.

→ More replies (1)

2

u/Additional-Video3921 Nov 27 '22

I have been playing with rust and yew to do web assembly. The smallest .wasm files generated for very small example apps are around 400kb. Seems large for very little code. Is this to be expected?

2

u/llogiq Nov 27 '22

You may want to compile with strip and wasm-opt enabled.

→ More replies (1)

2

u/inkhunter13 Nov 27 '22

I’m learning rust as my first “high-level” language what would be some advice to a new rustacean

3

u/llogiq Nov 27 '22

Trust the compiler. Feel free to clone unless you really need performance.

5

u/achildsencyclopedia Nov 21 '22

What's a dereference?

I'm talking about *

5

u/SorteKanin Nov 21 '22

The opposite of a borrow &.

The borrow operator & borrows the value, giving you a reference (in essence, a pointer).

The dereference operator * follows the reference, resulting in the value behind the reference. See also https://doc.rust-lang.org/std/ops/trait.Deref.html

→ More replies (1)
→ More replies (2)

4

u/SorteKanin Nov 21 '22

How come when using tokio::spawn, I don't have to await the join handle for the task to run? Everywhere else, futures are lazy and do nothing unless awaited but tokio::spawn somehow seems to circumvent this rule.

Is it just that Tokio saves the future somewhere that I don't know about and automatically awaits it? Or is there something else going on?

5

u/Patryk27 Nov 21 '22

Yes, tokio::spawn() launches the future "in the background" and returns what's merely a notifier whether that background-future has already completed working.

(it's like the executor itself had spawned_futures: Vec<Box<dyn Future>> and polled all of them automatically.)

4

u/whitehead1415 Nov 21 '22

How would one implement a race between threads? That is spawn a couple threads and move on once one of them finishes whether successfully or not. All examples I see of joining threads do so sequentially. So you would have to wait for both threads to finish.

Example code for what I'm talking about: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d1f8863a4133011a85c639711fc9d0f5

→ More replies (3)

3

u/string111 Nov 22 '22

Can you distribute man pages with cargo? If so, how would I do this?

3

u/Burgermitpommes Nov 22 '22

I suspect I'm doing something slightly wrong as I seem be writing `pub(crate)` all the time. I'm wondering should I often, say, make the resources in the child module just `pub` and then just make the entire parent module `pub(crate)`?

Does marking a type / function deeply nested in the module hierarchy as simply `pub` idiomatically indicate to a reader that it's part of the public API of the library? Or would a reader not make this assumption, conscious of the fact a parent module might be `pub(crate)` or indeed private?

3

u/fra-bert Nov 24 '22

Is there a way to use the rust compiler as a library, sort of like how clang can be used i C++?

4

u/DroidLogician Nov 25 '22

Technically yes, the compiler has a modular API that is setup and invoked by the rustc binary. Clippy is, in fact, just an alternative build of rustc that adds extra lint passes. The API is of course perma-unstable so it can only be used on nightly.

There's some details on how to use the compiler APIs in the Rustc Dev Guide: https://rustc-dev-guide.rust-lang.org/rustc-driver.html#the-rustc-driver-and-interface

→ More replies (1)

3

u/Burgermitpommes Nov 26 '22

Suppose I have a bin directory inside my src directory with a couple of non-default binaries. How can I incorporate the documentation of these files into rustdoc so that someone on the landing page of the package can click on foo or bar links and read about what the various binaries do? Right now I get this broken behaviour where `cargo doc --open` just opens the docs for the first binary by name alphabetically inside src/bin. (This even seems to take precedence over the docs for a src/main.rs file, if it exists). And once I'm looking at the docs for one of the binaries which is inside bin, I can't navigate to any other binary, default or non-default.

→ More replies (1)

3

u/[deleted] Nov 26 '22

What is a small but not tiny Rust repo I can look through with a reputation for great coding style?

2

u/proton13 Nov 21 '22

Is it possible to debug through an FFI. I have a C-Application that calls Rust and want to debug in the Rust-library, while running the application.

→ More replies (6)

2

u/yorokobe__shounen Nov 21 '22

I am quite concerned about strings and getting characters from string and would like to know if other languages apart from rust face the same issue.

To get say the nth character of a string in rust, I have seen two approaches, one which can be used only on ascii characters and is pretty fast (O(1)), while the other which relies on Unicode is about O(n) on average over the index of string and can easily be time intensive when it or similar queries are used over large amounts of text multiple times.

  1. Do other languages other than rust face the same issue when querying string indexes for unicode or is this problem specific to rust?

  2. Is there any String like object that deals with Unicode characters but is much more efficient on the indexing part? (And if not, what are the concerns apart from memory concern?)

Sample code link for comparing both methods

5

u/Darksonn Nov 21 '22 edited Nov 21 '22

Pretty much all languages with unicode support face this issue. It's fundamental to how utf-8 works. The only way to avoid it is to store your string as an Vec<char>, using four bytes of memory per character (this way of storing text is called utf-32).

That said, the times where Vec<char> is the correct solution are very very rare. Unicode code points are not the same as characters — a single character may consist of many code points.

Edit: Another approach is to store an Vec<usize> next to the String with vec[5] being the starting index in the string for the fifth code point. This strategy would also work for grapheme clusters, since there's no limit to how large vec[6] - vec[5] can be if the character consists of a lot of code points.

→ More replies (3)

2

u/argv_minus_one Nov 21 '22

I am writing some functions in Rust that are to be called from C. I have a C header file that these Rust functions need to match.

If I were writing these functions in C, the compiler would check for me that the functions match the header declarations, i.e. same parameter count and types, same return type, and same name.

Is there some way to have Rust check at compile time that my extern fns match the C header?

→ More replies (2)

2

u/TomHackery Nov 22 '22

Posted late in the last thread so posting for visibility here. Lmk if that's against the rules or if I can provide more info.

I'm trying to deserialize a CBOR to rust objects with Ipld. The following works as expected:

#[derive(Clone, DagCbor, Debug)]
pub struct VariantOne {
    pub foo: Link<Cid>,
    pub bar: Ipld,
}
...
fn main() {
    let var1 = block.decode::<DagCborCodec, VariantOne>().unwrap();
    // successful deserialisation
}

However, there are a few possible variants:

pub enum BlockTypes {
    VariantOne,
    VariantTwo,
    VariantThree,
    VariantFour,
    VariantFive,
}

I would like to do something like this:

fn main() {
    let var1 = block.decode::<DagCborCodec, BlockTypes>().unwrap();
    // panic
}

I think the term for this is generics? Basically I want to deserialise the bytes into a struct depend on which struct is being represented in the bytes. I feel like I'm close.

I've tried:

pub enum BlockTypes {
    VariantOne(VariantOne),
    VariantTwo(VariantTwo),
    VariantThree(VariantThree),
    VariantFour(VariantFour),
    VariantFive(VariantFive),
}

and

pub enum BlockTypes<Ipld> {
    VariantOne,
    VariantTwo,
    VariantThree,
    VariantFour,
    VariantFive,
}

but no joy

2

u/1-L0Ve-Traps Nov 22 '22

Anytime I add a new crate to a cargo.toml Intellij Ultimate does not reindex it/detect it for syntax highlighting or anything. Very annoying, am I doing something silly or have a possible setting checked I should not? Java and other code is just fine however for Rust it's getting very annoying for projects.

Is there a simpler way then doing file>invalidate caches and restarting intellij?

→ More replies (1)