Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
duck monster
Dec 15, 2004

notwithoutmyanus posted:

So I read the OP, and I know a beginner-ish level of javascript, a middling level of python, and I dabble a little bit in SQL queries. I've never really broken into full developer understanding of any language I think, maybe lacking fundamentals. Is rust okay for me to dive in and try to make that big jump or is that a bad idea at this point? I feel holistically my programming background is not at all up to proper practice, but I'd rather make the things I want to make myself than end up hoping someone else can, starting with some baby steps. I do know some folks in my circles who develop who have felt strongly Rust is the language to want to learn.

I wouldnt overlook Golang. Its a stupidly easy language. Though its got a few..... eccentric..... design choices.

Adbot
ADBOT LOVES YOU

kujeger
Feb 19, 2004

OH YES HA HA
As much as Python may/will be faster at prototyping stuff or whatnot, being really liberal with cloning in Rust will take away a lot of the initial speed bumps.

Remember: keep calm, call .clone() !

Ranzear
Jul 25, 2013

Wrap literally every variable in Rc and it'll still be faster than python.

notwithoutmyanus
Mar 17, 2009

prisoner of waffles posted:

seconded. if you want to learn programming in general and not in some specific arena that another language would do better (JavaScript or TypeScript would be this for web stuff, Rust would be one option for “””close to the metal”””, there’s other area-language pairings) Python is good.

On the other hand, Rust educational materials are supposed to be really good and extra guidance/constraints can be helpful when you start learning a generalist skill… the Rust books may presume some familiarity with programming concepts but it sounds like notwithoutmyanus has some of that familiarity?

Yeah I pretty much jumped right in on the book..Only weird thing to me so far is mentally processing :: and turbofish although I understand why it's there. I have done a little python and a bit more familiarity with JavaScript/Python as I used to write algorithms in JavaScript but I always struggled with large functions and never really built my own code incorporating libraries in a long time. So rust kinda fills the perfect niche it seems. I don't mind having to define types for variables, and before I even read the book at all I was kinda trying to write up a framework for what I wanted to grab from API 's by making a struct with all the variables underneath the API would spit out. Not sure if it was the right idea or not but it did make sense immediately.

Edit: I did look at golang a little but eh. I will stick with this.

notwithoutmyanus fucked around with this message at 13:30 on Dec 30, 2022

Ranzear
Jul 25, 2013

To call the turbofish a red herring of learning rust seems too on-the-nose. It takes a relatively high level example to show a use case that isn't more obviously solved by putting a type on a let declaration somewhere.

It comes up in method chaining mostly, specifically a method chain in a match statement or something where somebody just wants to avoid an extra binding, which can definitely be a thing if that match function is running in an iterator.

notwithoutmyanus
Mar 17, 2009
Well I haven't seen any other languages using :: or <> like that. In that sense I was more used to JavaScript with function.thing

Love Stole the Day
Nov 4, 2012
Please give me free quality professional advice so I can be a baby about it and insult you

notwithoutmyanus posted:

Well I haven't seen any other languages using :: or <> like that. In that sense I was more used to JavaScript with function.thing

MyStruct::my_function is like MyClass.prototype.my_function you probably already know this idk

notwithoutmyanus
Mar 17, 2009

Love Stole the Day posted:

MyStruct::my_function is like MyClass.prototype.my_function you probably already know this idk

right, but for me I internalize a lot of poo poo that I probably shouldn't, so it's basically me reading it going "this is different from (this)". Not saying it is, or that it's a problem with rust, it's me in processing what I see weirdly. To be clear, it's me being more a newbie to rust than some sort of "rust" issue.

notwithoutmyanus fucked around with this message at 18:59 on Dec 30, 2022

notwithoutmyanus
Mar 17, 2009
Does anyone have a good reference for functions with Dyn/when to use/when not to use?

gonadic io
Feb 16, 2011

>>=
In what sort of contexts, when defining your own traits or when using stdlib ones? The key thing to remember vs impl or generics is that the method taking a dyn does not know which concrete instance it is. This is in contrast to generics or impl, where at compile time the compiler works out which instance it is and hardcodes that.

The advantages to not knowing which concrete instance you have, only a pointer and a defined vtable interface, are things like: you can switch at runtime which instance you're using, it breaks infinite recursive type loops, code size gets smaller because there is only one method for all possible instantiations vs one for each concrete one. Types and type errors become easier as the compiler can't "see through" the dyn.

The downsides are mostly that it's slower as the compiler can't take advantage of knowing more about what the type actually is, and there's an extra level of runtime indirection.

Dominoes
Sep 20, 2007

notwithoutmyanus posted:

Does anyone have a good reference for functions with Dyn/when to use/when not to use?

My ROT: If you use a `Fn` trait and you get compile errors, prepend `dyn`, If that doesn't work, also wrap it in a box. If you give up, use a `fn` pointer instead.

IMO this is one of the more poorly-designed parts of the lang.

Music Theory
Aug 7, 2013

Avatar by Garden Walker
Nah, I think it's fine. You use dyn if you have a structure or a function whose input/stored type can vary at runtime.

Ex. If you want to store a bunch of things that implement a trait, and those things may be of different types, you'd use a Vec<Box<dyn Foo>>. (The Box is required because Vecs store data in a contiguous array, and the sizes of the dyn Foo's may vary.)

Music Theory fucked around with this message at 17:58 on Jan 14, 2023

Dominoes
Sep 20, 2007

It's IMO hard to grok compared to other parts of the lang. I can do it now, but only by C+P existing code, or trial+error with the compiler. It's an incantation.

lifg
Dec 4, 2000
<this tag left blank>
Muldoon

Dominoes posted:

My ROT: If you use a `Fn` trait and you get compile errors, prepend `dyn`, If that doesn't work, also wrap it in a box. If you give up, use a `fn` pointer instead.

IMO this is one of the more poorly-designed parts of the lang.

I hate that I’ve been using Rust for a few months now and I have no idea what this means.

Dominoes
Sep 20, 2007

It's for higher-order functions. Ie passing functions themselves as arguments. Comes up when making APIs.

Actually, this is a trend. A lot of the messier / harder to understand parts of Rust come up mostly when making APIs, and may be hidden from consumers of APIs or application programming. (See also: traits and to a lesser extent macros)

lifg
Dec 4, 2000
<this tag left blank>
Muldoon
I’m working with a couple giant RESTful actix apps. I guess if I looked under the hood more I’d see those higher order functions in action.

gonadic io
Feb 16, 2011

>>=
Users only generally see them when boxing traits or getting type errors from future executors and so on

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
After an absence, I'm back to fuss over the square pegs and round holes I have to deal with in wide strings.

I have this:

code:
let values: Vec<*const u32> = vec![w_json_data.into_raw(),];   // w)json_data is widestring::WideCString
I needed that to pass wchar_t** to something, which is done with values.as_ptr()

The problem now is that element is raw and I need to reclaim it with from_raw(), but I can't figure out how to iterate it back from *const u32. Does anybody know the casting?

In this case, I can actually skirt around this because I can just do the raw cast outside the Vec declaration and then free it there. The problem is I particularly run into issues right here with wchar_t** conversion to_from so I just wanted to ask anyways for later.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
Doesn't WideCString convert to a *u16? Why do you have u32 in there?

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
TBH I don't really know either. I think it's doing UTF-32 by default for WideCString. The compiler insists that into_raw() produces a *mut u32. Heck, I was thinking about that earlier:

code:
        let raw_json_data = w_json_data.into_raw();

        let values: Vec<*const u16> = vec![raw_json_data,];
code:
error[E0308]: mismatched types
   --> src/lib.rs:564:44
    |
564 |         let values: Vec<*const u16> = vec![raw_json_data,];
    |                                            ^^^^^^^^^^^^^ expected `u16`, found `u32`
    |
    = note: expected raw pointer `*const u16`
               found raw pointer `*mut u32`

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
Nevermind, my web search linked me to an ancient version of the crate docs. That always trips me up.

Why are you using a Vec? Can't you just create your double-pointer by using & on the raw pointer?

Ranzear
Jul 25, 2013

Out of date docs will usually flashbang me but it's happening less lately. Really needs something better than orange text in the top corner.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!

Jabor posted:

Why are you using a Vec? Can't you just create your double-pointer by using & on the raw pointer?

I don't remember why particularly but I just remember crashing and burning after trying anything else. It has been a while so maybe I can work it out correctly if I try again fresh. The function I am calling is just taking wchar_t** and I am only giving it one element anyways.

Edit: Update The function I'm calling is from C and my bindings.rs has a signature of *const *const wchar_t. The variable I'm starting with is *mut WideChar. I can't seem to make the cast (it gets mad turning mut into const in particular). How do I make that cast without using a Vec as an intermediary?

Rocko Bonaparte fucked around with this message at 19:05 on Jan 18, 2023

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
Separate topic: is there a mocking tool in Rust with the same power as the mock library in Python? That library lets you call out replacements for individual function calls that are layers within code blocks that are otherwise invisible.

Yes, I have some issues dogmatically with that mocking method, but I would rather not totally refactor this trash code (as you have probably assumed by my posts) with all the interfaces I need to have non-system calls for testing.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I'm really shitposting about Rust today!

I figured out how to cast that double pointer without needing a Vec:
code:
        let keys: *const *const WideChar = &(raw_bridged_data_str as *const WideChar);
Easy peazy, right?

I got another problem: Some of my wide strings are leaking, but valgrind is only showing symbols into widestring itself. I have debug symbols in my library and application so I don't know what the problem is. Is there something special I have to do? For what it's worth, I got valgrind from source (3.21.0.GIT) since I saw some complaints about older versions with Rust binaries--something to do with the static compilation. It didn't make any difference. Some of the stuff it originally reported definitely came from my source. I had a few into_raw() calls that needed from_raw() calls to reclaim the memory, and the associated output disappeared from subsequent runs after fixing it. So it's not like these leaks originate literally in the widestring source.

gonadic io
Feb 16, 2011

>>=
You simply cannot cast your Vec<u16> to Vec<u8> (or equivalently your string to widestring) or vice-versa. You must create a new allocation and copy over.

This is very likely what is causing the leak if I have understood your question right (phoneposting sorry).

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I don't think that cast is the specific problem. The WideChar type is u32 but I'm just using the proper name for the platform. The character size isn't getting screwed with. At any rate, I didn't get a new leak from making that casting change. They're coming from some from_str() and clone() calls in more numbers than I have than that cast. I guess that's implying that yeah, I could have still screwed up, but I'm screwing up elsewhere too for unrelated reasons. I wish it would just tell me at what file and line number since it's at least able to see it up to the entrance into the widestring source each time.

I was just posting the cast because it looks like I figured out how to declare the cast without, say, putting it into a Vec first and giving the C call a raw pointer to its contents.

gonadic io
Feb 16, 2011

>>=
I see. Looking more closely I don't see any issue with that cast yeah.

What about c-str to rust-str conversions? Are you leaking the trailing nulls perhaps? In your app, how do the actual contents of the string appear in c and rust, all look fine?

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
All my output looks fine, and I've been printing out the strings I keep or otherwise transmitting them over a network using another library. My main concern is actually a crash that seems to be coming in OpenSSL cleanup from that other library (written in C++ with C bindings). Leaks aren't going to solve a double free, but I am trying to clean them up because I've seen that affect where a crash ultimately happens due to removing potential time bombs.

Getting into more details, I take the *const c_char I get from the parent C code, and use CStr::from_ptr() to wrap it. I immediately then copy it to a widened version using to_string_lossy, cleaning out characters out of range, then converting it to a WideCString using from_str(). That should be making a copy.

We can delve into that and look over things with a magnifying glass (though I can't post all the code) but I'd probably get a lot closer if valgrind would just tell me exactly where I blew it and leaked. Since I'm coming in after the fact, I have something like 700 lines of code that could be suspect at any point. I suppose if I were running valgrind as I went, I could have caught these with the vague output I got, but I didn't. :(

Subjunctive
Sep 12, 2006

✨sparkle and shine✨

Rocko Bonaparte posted:

I suppose if I were running valgrind as I went, I could have caught these with the vague output I got, but I didn't. :(

Can you bisect with git (probably manually)?

gonadic io
Feb 16, 2011

>>=

Rocko Bonaparte posted:

All my output looks fine, and I've been printing out the strings I keep or otherwise transmitting them over a network using another library. My main concern is actually a crash that seems to be coming in OpenSSL cleanup from that other library (written in C++ with C bindings). Leaks aren't going to solve a double free, but I am trying to clean them up because I've seen that affect where a crash ultimately happens due to removing potential time bombs.

Getting into more details, I take the *const c_char I get from the parent C code, and use CStr::from_ptr() to wrap it. I immediately then copy it to a widened version using to_string_lossy, cleaning out characters out of range, then converting it to a WideCString using from_str(). That should be making a copy.

We can delve into that and look over things with a magnifying glass (though I can't post all the code) but I'd probably get a lot closer if valgrind would just tell me exactly where I blew it and leaked. Since I'm coming in after the fact, I have something like 700 lines of code that could be suspect at any point. I suppose if I were running valgrind as I went, I could have caught these with the vague output I got, but I didn't. :(

That all sounds fine to me too. Are there any points where you explicitly pass the string len as a parameter? How does it get from the WideCString to the *mut WideChar you described earlier (and how do you get that len?). I assume valgrind is showing you how large each leak is, is it 1 byte at a time i.e. probably the null terminators, or e.g. half the len of the string, or something random?

Subjunctive
Sep 12, 2006

✨sparkle and shine✨

How would you leak just the NUL terminator? Isn’t that part of the same allocation as the rest of the string?

gonadic io
Feb 16, 2011

>>=
I'm thinking if the length somehow gets passed as 1 shorter due to not counting it, then when it gets free'd the last byte stays unfree'd. Depends if the allocator is keeping track itself or trusting the len you give it. Might be totally off-base here, not the most experienced in these areas.

Subjunctive
Sep 12, 2006

✨sparkle and shine✨

Almost always the allocator just gets an address for free, and not a size; the size might not even be known at deallocation time in the case of trait references and such. Most allocators store the size of an allocation in a little bit of memory right before the pointer that gets returned from allocating, if they need to track it at all.

gonadic io
Feb 16, 2011

>>=
I see, good to know. I've got no idea then.

gonadic io
Feb 16, 2011

>>=
Okay then the main avenue left is you turning some allocation in rust into a pointer to give to C, but then either freeing it from C (bad) or failing to have the C return it to free it from rust (a leak)

Subjunctive
Sep 12, 2006

✨sparkle and shine✨

If valgrind is telling you the address that leaked, and it’s consistent (disable ASLR? hmm), you could set a conditional breakpoint in the allocator for it returning that address and then see what’s being allocated then.

If it’s not consistent, you can break on valgrind printing out the leaks and manually inspect the memory at that address to see if you can tell from its type info or contents what it is.

Maybe try rr if you’re on a platform that supports it? That would let you go back in time to when the leaked address was allocated, even if the address isn’t consistent.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I mean if I'm really gunning for it and I just can't get valgrind to print everything, I think I could set breakpoints at the reported points in the stack, find an input matching the rough size, and be pretty confident I'm at the scene of the crime. I also have some ways of running parts of the code in isolation; I don't have full unit testing (see my mock question) but I do have a utility that hits subsets of most of the code.

Some of the sizes are conspicious. Nothing is just, like, 1 byte. However, some of the sizes are, like 1700 kB and that smells like my JSON message. There's only a few places that could go wrong. So I can probably walk through it and see if I am hitting the offending areas there. One of them is that helper to making a WideCString from &CStr. There's a Cow<> in there and I see a ~1700kB clone not getting freed so my nostrils smell a problem right there.

All of that is still about being clever since I can't get it the straighforward way, which I was just hoping was solvable. It looks like questions about valgrind and Rust come up from time to time online in various places (including rust-lang.org) but nothing I've seen in them has helped me so far.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
Is there a method I could use to, say, instrument some of the widestring code to gauge when some of these leaked items are coming up? I have some leaks coming out of a clone() function and I want to break on it if it's got at least a certain number of characters. Whatever I do, the changes don't reflect in the build. I saw a suggestion that I adapted from online:
code:
cargo clean -p widestring
It looks like it does something to widestring when I build again, but I don't even get a basic hello print. The debugger is also pretty confused with what I'm doing.

I understand that this isn't the kosher way but it's not like I actually intend to make a permanent change to a dependency here or anything, so I figured there was some hack I could use.

Adbot
ADBOT LOVES YOU

gonadic io
Feb 16, 2011

>>=
worst case just fork the widestring repo and point your cargo toml at it. cargo does cache local copies in your $HOME, and that command gets rid of local build artifacts to force it to build from that local cached source copy (which you presumably are supposed to have edited by this point). then after running cargo clean you would build and run the binary as you previously were, you'll see the additional steps of the widestring lib being recompiled.

gonadic io fucked around with this message at 19:31 on Jan 19, 2023

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply