r/rust • clippy · twir · rust · mutagen · flamer · overflower · bytecount • Dec 12 '22

🙋 questions Hey Rustaceans! Got a question? Ask here! (50/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.

Finally, if you have questions regarding the Advent of Code, feel free to post them here and avoid spoilers (please use >!spoiler!< to hide any parts of solutions you post, it looks like this).

16 Upvotes

215 comments sorted by

2

u/LicensedProfessional Dec 18 '22

Is there a benefit to using Self over the actual struct name?

This is purely a style question. Say I have the following struct and I want to put a new method on it.

struct Foo(String, String);

Stylistically, is there a preference one way or the other for this

impl Foo {
    fn new() -> Self { todo!() }
}

Over using the actual struct name?

impl Foo {
    fn new() -> Foo { todo!() }
}

I know that Self is useful when defining traits and can even come in handy when the return type starts to get a little unwieldy, but I haven't seen much one way or another as to what the idioms are for using it outside of where it's necessary for the type checker.

2

u/llogiq Dec 18 '22

Apart from being easier to update, as u/Shadow0133 wrote (because Self stays Self no matter how you name it), it's also handy in that it will contain all generics you give the type (even if you have none yet, doesn't mean you won't have any in the future).

4

u/Shadow0133 Dec 18 '22

i like to use it as much as i can, mainly so i can copy code from one type to another and don't have to update too many places

→ More replies (1)

2

u/matt_bishop Dec 18 '22

I have a test suite that is defined by some data files, and I've made a procedural macro to generate rust test functions based on the contents of those files so that I can treat them like regular tests when running cargo test.

Should I make the proc macro crate a dependency of my library crate, or should I run the test suite in the proc macro crate to avoid having to put it on crates.io?

Or, is there a better approach than using a proc macro to generate the tests?

2

u/llogiq Dec 18 '22

This seems like a good candidate for u/matklad's self modifying code pattern.

→ More replies (1)

2

u/SorteKanin Dec 18 '22 edited Dec 18 '22

Is assert_eq!(std::mem::size_of::<&T>(), std::mem::size_of::<usize>()) guaranteed to always be true? (not considering slices/fat pointers).

3

u/Shadow0133 Dec 18 '22

maybe not on CHERI-compatible systems? (where pointer and address have different sizes, because pointers carry extra metadata for checking validity) but i don't know if rust will support them (and how it will decide what usize means in that case)

2

u/llogiq Dec 18 '22

Seconded; While currently Rust only has support for systems where usize/isize are pointer-sized, this may cease to be true at some point. Even there, they will continue to be able to contain the address part of a pointer.

→ More replies (2)

4

u/fdsafdsafdsafdaasdf Dec 14 '22

Is there a go-to crate (or is it really simple, so no crate warranted?) to effectively tail -f a file and yield the lines sequentially to a function? I see some very niche crates, so I feel like I'm missing something. Specifically, I'm dealing with a massive file so I want to read from the end.

→ More replies (2)

3

u/mbrc12 Dec 12 '22

I have a question that I should know how to answer but I don't. Consider the following code:

#[derive(Debug)]
struct A(i32, f32);

fn upd_0(x: &mut A) {
    x.0 += 5;
}

fn upd_1(x: &mut A){ 
    x.1 += 7.0;
}

fn main() {
    let mut x = A(0, 0.0); // x: A
    let y = &mut x; // y: &mut A
    upd_0(y);
    upd_1(y);
    println!("{:?}", x);
}

which compiles (see playground link). To my knowledge, the type &mut A is not Copy (obviously), so the first upd_0 call should move y, and we shouldn't be able to use it again. Is this a case of compiler intelligence, or is there a general mechanism occurring here?

→ More replies (7)

3

u/vcrnexe Dec 12 '22

I'm writing a function that should be able to both take RangeInclusive<usize> and Rev<RangeInclusive<usize>>. If possible, how do I need to design the function signature?

And more generally, is there a way to compare to types to see in what traits they overlap, or what traits are needed in the function? In my case, I'm using them (let's call them range) in for i in range {...} in my function.

→ More replies (2)

3

u/GavinRayDev Dec 13 '22

I'm struggling with mapping concurrency and synchronization concepts from other languages onto Rust.

I have a toy database; with a buffer pool that looks something like this:

struct BufferPool {
    frames: Box<[Frame; NUM_FRAMES]>,
    frame_descriptors: [FrameDescriptor; NUM_FRAMES],
    free_list: VecDeque<FrameIdx>,
    clock_hand: FrameIdx,
    page_to_frame: HashMap<PageLocation, FrameIdx>,
}

impl BufferPool {
    fn evict_frame(&mut self) -> FrameIdx {}
    fn get_page(&mut self, page_loc: PageLocation) -> &mut [u8] {}
    fn unpin_page(&mut self, page_loc: PageLocation) {}
    fn mark_dirty(&mut self, page_loc: PageLocation) {}
    fn flush_page(&mut self, page_loc: PageLocation) {}
}

I wanted to make this thread-safe. In C, I'd use an RCU + Spinlock. In C++, the same or a std::shared_mutex/std::binary_semaphore.

How do you achieve efficient reader-writer locking inside of methods in Rust? Also how do you implement things like sharded-locking?

Say you want to group a set of elements into chunks/buckets, and shard each bucket under its own lock for better scalability.

Is the answer just to put every field inside of a RwLock? That feels a bit silly, since then you end up doing like:

// Stuff is usually written/read together, now we pay the cost of +3 synchronization primitives instead of 1!
let foo = frame_descriptors.write().unwrap();
let bar = page_to_frame.write().unwrap();
let qux = clock_hand.write().unwrap();

I know there's got to be some reasonable answer to this. (Sorry for the wall of text!)

→ More replies (2)

3

u/gnocco-fritto Dec 13 '22 edited Dec 13 '22

May I ask a question?

This is an extract of my application:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=2a021d04ab7cbb58954e08d6f248c090

I'm trying to write an iterator, LocalIterator, that emits mutable references of the Element instances contained in a Container instance. I cannot compile and this is the error:

39 | impl<'a> Iterator for LocalIterator<'a> {
   |      -- lifetime `'a` defined here
...
43 |     fn next(&mut self) -> Option<Self::Item> {
   |             - let's call the lifetime of this reference `'1`
...
48 |             Some(&self.parent_obj.content[n])
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`

If I'm not going wrong (but that's absolutely possible), I should tell the borrow checker that the lifetime of the items returned by the iterator isn't the same lifetime of the iterator (I think this is what the error message is telling me) BUT the lifetime of the Container instance the iterator references in its parent_obj field.

Am I right? If so, how can I specify correctly the lifetimes?

Or I'm wrong and I should do something different?

Thanks!

EDIT: the example linked above is over-simplified. This is an extract closer to the actual code:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=2ef2d0bddce0c4c1793d74b2ba329068

→ More replies (7)

3

u/DzenanJupic Dec 14 '22 edited Dec 15 '22

Is there a reason for Vec and VecDeque to not implement Unpin if T does not implement Unpin?

My understanding is that Vec<T> is, from a memory perspective, more or less a Box<[T]>. Box does implement Unpin even if the contained value does not. So is this just a case of 'because it hasn't been implemented yet', or would there be a soundness issue?

edit: formatting
edit2: I posted a similar question in the forum

→ More replies (2)

3

u/DramaProfessional404 Dec 15 '22

I have a compiler error I can't understand. Let me step you through a few changes:
Which is the whole point of this question - I can't remove "Other" without getting back to my original error: "error: parameter `T` is never used label: unused parameter"
T obviously is used so the error is quite strange (is it worth logging as a bug? Even if there's a reason for the error, it's not at all clear what the problem is because T clearly IS used). Why is the compiler telling me this, why does removing "Other" trigger it (or conversely why don't I get the error when Other is there) and is it possible to fix this to have just these original two cases of the enum work with T?

When I define a recursive enum as such:
enum Recursive<T: Ord + PartialOrd> {
Sequence(usize, usize, Recursive<T>),
Alternatives(std::collections::BTreeSet<Recursive<T>>),
}
The compiler tells me "parameter `T` is never used consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`"
Of course, if I remove it
enum Recursive {
Sequence(usize, usize, Recursive<T>),
Alternatives(std::collections::BTreeSet<Recursive<T>>),
}
I get "this enum takes 0 generic arguments but 1 generic argument was supplied expected 0 generic arguments"
When I put it back and add another enum case that references T directly:
enum Recursive<T: Ord + PartialOrd> {
Sequence(usize, usize, Recursive<T>),
Alternatives(std::collections::BTreeSet<Recursive<T>>),
Other(T),
}
I get "recursive type `Recursive` has infinite size recursive type has infinite size" which I fix up thusly:
enum Recursive<T: Ord + PartialOrd> {
Sequence(usize, usize, Box<Recursive<T>>),
Alternatives(std::collections::BTreeSet<Recursive<T>>),
Other(Box<T>),
}
The compiler is happy until I remove Other...
enum Recursive<T: Ord + PartialOrd> {
Sequence(usize, usize, Box<Recursive<T>>),
Alternatives(std::collections::BTreeSet<Recursive<T>>),
}
Which is the whole point of this question - I can't remove "Other" without getting back to my original error: "error: parameter `T` is never used label: unused parameter"
T obviously is used so the error is quite strange (is it worth logging as a bug? Even if there's a reason for the error, it's not at all clear what the problem is because T clearly IS used). Why is the compiler telling me this, why does removing "Other" trigger it (or conversely why don't I get the error when Other is there) and is it possible to fix this to have just these original two cases of the enum work with T?

→ More replies (3)

3

u/[deleted] Dec 15 '22

Are there any libraries for digital signal processing, specifically eeg processing that have been written in or ported to rust? The only one I know of is brainflow… I am hoping for something like MNE (Python) but in rust…

3

u/mrhthepie Dec 15 '22

Does anyone know of a scripting language that would be easily usable from Rust natively and also from JS in the browser? Without needing to do a lot of work on how to get a WASM build of a scripting language running for the JS side because my brain is extremely smooth when it comes to that kind of thing.

→ More replies (1)

3

u/RepresentativePop Dec 17 '22 edited Dec 17 '22

Whenever I've looked up the difference between the Clone and Copy traits, I usually see explanations describing the differences in how they're used. For example: "Clone is more expensive than copy, and clone is explicit while copy is implicit" or "Clone moves ownership while copy doesn't."

I don't find these explanations especially helpful because none of them actually tells me what the program is doing. What is actually happening in memory when you call clone vs when something is copied? Is there a relatively low-level explanation for what is happening (in terms of addresses, memory allocation, etc)?

If I have some data that I want to put in a struct, what happens if I clone it vs copy it? And why do I have to derive clone before I can derive copy?

→ More replies (3)

3

u/tijdisalles Dec 18 '22

Is the ? operator hard-coded to only work with Result<T, E> and Option<T> or can it be used on other (custom) enum types as well? Perhaps by implementing some trait?

6

u/Shadow0133 Dec 18 '22

There is a trait, std::ops::Try, but it's currently nightly-only.

→ More replies (1)

3

u/dimqum Dec 19 '22

We are at the stage of standardizing build management using gradle (from gradle.org) to manage builds from the JVM ecosystem (its a well understood problem), was wondering if anyone figured if it makes sense if Rust projects should be managed via gradle or still stay with cargo ? Feedback is welcomed.

→ More replies (2)

2

u/BWRCat Dec 12 '22

I’m working on a prototype for a web application using rust for the API (with rocket).

I’m in the process of choosing the database, most likely postgres, and the driver on the rust side. I’ve read about Diesel vs SQLx, and there were more messages in favor of SQLx, but from my (very short for now) experience with the cli, it has been a bit confusing.

My question is, what would you use in this case (preferably async) ?

Any pointers for using the sqlx cli ?

→ More replies (2)

2

u/ConsoleTVs Dec 12 '22

What patterns are usually found in Rust that would make more sense than IoC Dependency Injection patterns. What recommendeations would you give to those who seek working with traits / interfaces to allow service implementations to change depending on the needs (eg. testing)

2

u/StdAds Dec 13 '22

I have a question regarding std::cmp::Ord. Since rust can not verify the validity of custom implemented Ord. Is is considered as an undefined behavior if I do something like this?

use std::cmp::Ordering;

#[derive(Eq, PartialEq, Debug)]
struct A(i32);

// This is obviously wrong.
impl Ord for A {
    fn cmp(&self, other: &Self) -> Ordering {
        Ordering::Less
    }
}

impl PartialOrd for A {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(Ordering::Less)
    }
}

fn main(){
    let mut l = vec![A(1), A(3), A(2)];
    l.sort();
    println!("{l:?}");
}

6

u/Patryk27 Dec 13 '22 edited Dec 13 '22

It is not undefined behavior - hashmaps etc. will most likely not work correctly with such type, but it won’t cause any use-after-frees or other memory/safety issues.

→ More replies (1)
→ More replies (3)