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
Linear Zoetrope
Nov 28, 2011

A hero must cook
So what's been going on in Rust-land in the last bit? Any major changes or features? I've been stuck in Python-land for research aside from some graphics projects with gfx and I'm wanting to get back into some Rust stuff.

Adbot
ADBOT LOVES YOU

Linear Zoetrope
Nov 28, 2011

A hero must cook

Ralith posted:

"just a macro" can't do this. At a guess, the macro expands to a custom derive, which IIRC can run arbitrary code. Understanding what the macros you're using do is good practice regardless, of course.

Yeah, if you look at the source you get this:

code:
macro_rules! infer_table_from_schema {
    ($database_url: expr, $table_name: expr) => {
        #[derive(InferTableFromSchema)]
        #[infer_table_from_schema_options(database_url=$database_url, table_name=$table_name)]
        struct __DieselInferTableFromSchema;
    }
}

Linear Zoetrope
Nov 28, 2011

A hero must cook

gonadic io posted:

I think the need for macros will go down a bit once const_fn is stable. Certainly that's what I mostly use them for myself.

And impl Trait, one of the areas I used macros the most is where I basically wanted to just treat heterogeneous objects as a uniform trait and a trait object was insufficient. (Such as selecting between and initializing different implementations of the same interface based on user input, and then doing the same thing to it regardless).

E:

Like

code:
let program_behavior = match InputEnum::from_args(blah) {
   Foo => { let mut foo = initialize_foo(other_args); 
            foo.more_setup();
            foo
           },
    Bar => initialize_bar(other_args), 
};

program_behavior.do_thing();
Couldn't do that before because you can't return a trait object for foo or bar without it going out of scope so you basically had to repeat the code for everything you wanted to do within the match or use a macro.

Linear Zoetrope fucked around with this message at 14:48 on Aug 14, 2017

Linear Zoetrope
Nov 28, 2011

A hero must cook
Does anyone know how to test libloading plugins? I have the following sort of thing:

[Generic language] -> Plugin Loader/Context manager/etc (extern C no mangle cdylib Rust crate that uses libloading) -> Rust plugins (no mangle dylib Rust crate)

I'd basically like to test the Plugin loader, but to do that I need a compiled plugin. I've screwed around with making a dylib plugin_test crate and putting it in dependencies and dev-dependencies, but I've crawled all around the target directory and it looks like if it's written in Rust it's automatically linked statically as a .rlib and there's no .dll/.so/.dylib in sight. Additionally, Cargo doesn't (yet) support test-specific build scripts.

It's not the end of the world to write some scripts to run on Travis/Appveyor that manually build a test crate and set some test environment variables, but I was hoping for something a bit more Cargo-friendly.

Linear Zoetrope
Nov 28, 2011

A hero must cook
Cargo is easily Rust's best feature. Every time I have to dive back into C++ or Python land (and I'm a PhD student doing research work, so a lot of these aren't exactly... iron clad libraries) I end up with weird messes of dependency management. ESPECIALLY around the fuzzy OS-level boundaries where something works on one OS but suddenly the nice pip or setup.py or apt-get dependencies or whatever become a giant nightmare on OS X or Windows or not-Windows or Cent OS (but not Ubuntu) and so on. Rust doesn't prevent this, I've seen a few niche libraries that require some sys-type crates that they could only get working on one or two OS's or require installations of a language that are a pain to properly setup on Windows or something, but the mix of Cargo and Rust's general platform agnostic-ness makes it a hell of a lot less likely, and IME crate maintainers in Rust-land are much more likely to document these idiosyncrasies.

Rust isn't a perfect language, but once you learn how to cope with how long you spend before getting a large engineering effort to compile and how to deal with the corner cases where the borrow checker gets really stupid it's one of the most pleasant environments to work in.

(Also, I have to at least give a shoutout to Rust having very firm community guidelines wrt kindness, inclusiveness and recent initiatives to get women, transpeople, POC, etc into the core community with things like impl Future. It's really nice to know they're at least trying and it makes getting help with Rust way less draining.)

Linear Zoetrope
Nov 28, 2011

A hero must cook
Non-lexical borrow scopes are finally under review

Linear Zoetrope
Nov 28, 2011

A hero must cook

Beamed posted:

Double post, am I doing Rust right if almost all my mods end up being "pub mod"? Not sure how to expose, e.g., a crate one mod exposes without at least making it pub.

External crates are almost always put in lib.rs/main.rs. The only real exceptions are generated code that can't always rely on a dependency being available in a given location (and thus generate an extern crate declaration in the generated block).

Linear Zoetrope
Nov 28, 2011

A hero must cook

Beamed posted:

Which raises my question, I guess - I have extern crates which bring in dependencies for my controller/views on a given platform, but ideally later I'll have a second controller available with completely different dependencies. Things are definitely much nicer if I reference these in main for now, but I'd ideally like platform-specific view/controller to go to the specific controller, and so only bring in crates there. Guess it's a matter of if I care enough to create wrappers for those dependencies, huh..

I mean, you can do that, you just have to make sure that the crate is only referenced in child crates of the one you import it from. No need for pub then.

Something like

code:
unix/mod.rs <-- extern crate here
unix/controller.rs

windows/mod.rs <-- extern crate here
windows/controller.rs
And so on.

Linear Zoetrope
Nov 28, 2011

A hero must cook
Has anyone else had issues getting std::fs::remove_dir_all working on Windows? I get OS error 5 by doing something as simple as creating a directory tree and trying to remove it. I've been resorting to a #[cfg(windows)] patch function that just delegates to spawning a command running rmdir but that's obviously not... ideal.

Edit: I figured it out, I was using the created tree to clone a git repository into and git was setting permissions weirdly. Had to use walkdir to manually set the cloned files to not be readonly :shrug:.

Linear Zoetrope fucked around with this message at 04:19 on Feb 21, 2018

Linear Zoetrope
Nov 28, 2011

A hero must cook
You can just clone the repository or download the zip from crates.io, place it in a folder called "vendor" and use a path dependency.

All crates.io really is is a collection of crate zips with some heuristics about which versions are allowed to be used as dependencies.

Linear Zoetrope fucked around with this message at 21:50 on Apr 6, 2018

Linear Zoetrope
Nov 28, 2011

A hero must cook
Also don't feel bad because ndarray is really finnicky and not exactly the most ergonomic crate. Rust's type system isn't really built to handle the sorts of things mathematical crates really need right now, and they're not going to be worked on much until next year (given the roadmap and me asking the Rust team about future goals). They're blocked on a few things like type-level numerics.

Linear Zoetrope
Nov 28, 2011

A hero must cook
You can specify dependencies as optional based on an arbitrary cfg.

So in your Cargo.toml

code:
[target.cfg(not(wasm-unknown-unknown)).dependencies]
my_problematic_dependency = "1"
Then everywhere you use it in the code you can preface it with a #[cfg(not(wasm-unknown-unknown))]

Linear Zoetrope
Nov 28, 2011

A hero must cook
Blugh, the RLS being borked is killing me right now. Can't use old versions because I migrated to some 1.26 features.

Linear Zoetrope
Nov 28, 2011

A hero must cook

VikingofRock posted:

This is a fun bit of obfuscated rust from reddit:

code:
fn main() {
  println!("{:?}",
    (|&__@_:&'_ _|->_{[(|(_,__,..):(_,_)|__..__)(__)]})({&({||('"');}<=*&(),&[..=..])}));
}
See if you can figure out what it prints! I got a good bit of it, but there was some syntax in there that I was unfamiliar with. I didn't realize that .. is a valid struct which can itself be the endpoint of a range.

As with most code like this, it's easier if you add some whitespace, in which case you can start picking at pieces very easily. I wrote an explanation/my notes while deciphering here. Turns out I got it right!:

https://gist.github.com/LinearZoetrope/7e1cf85c74ef4ce813331336fc628593

The part that was surprising to me was that __ is a valid variable name, I knew The Range variants were a structs, but not that they had no restrictions on their input.

Linear Zoetrope
Nov 28, 2011

A hero must cook
I don't believe Rust has scientific notation literals, and you can't do things like that until const fn stabilizes. The workaround right now would be the lazy_static crate.

Linear Zoetrope
Nov 28, 2011

A hero must cook
Is that recent? It's not in the Rust Reference or the book afaict.

E: I was looking at the 1.7 Reference...

It's in the current one
https://doc.rust-lang.org/reference/tokens.html?highlight=literals#number-literals

Linear Zoetrope fucked around with this message at 22:31 on Sep 27, 2018

Linear Zoetrope
Nov 28, 2011

A hero must cook
Basically, Rust does untyped infinite-precision arithmetic on literals, but this is limited to the built in operators (+, -, *, /, and for ints, %, |, ^, &, <<, >>).

code:
let mut x = 5;
x = x + 12;

let y: () = x;
Will tell you x is of type {integer} which is an infinite-precision int. The variable will remain this pseudo-type until x is coerced into a type implicitly by assigning it, calling a function, etc. All of these operations on these pseudo-types are done at compile time, hence why they're limited to built-in operators. You're not allowed to call functions like powi until the exact runtime sized type is known (f64). There's an argument that since you're assigning to an f64, the compiler's type inference should be able to work out you mean f64, but there's also an argument for explicitness that fits with Rust's general philosophy (such as requiring you to manually type convert from an f32 to an f64 if you're doing let result: f64 = my_f64 + (my_f32 as f64)).

It's on the roadmap for Rust to eventually allow compile time functions, const fn which signals a function contains an algorithm that may (but not must) be run at compile/startup time. These are usually basic math and initialization functions (such as building a new global Mutex), but there hasn't been agreement on exactly how it should work and what the restrictions should be. See the tracking issue here.

Until then, the lazy_static crate provides a macro that sorta does that. Instead of a const it declares a static wrapped in a function that's called automatically the first time the variable is read. This theoretically has some minor runtime overhead, but it works in most cases. There may be an argument against using a number declared in lazy_static in a hot loop, but I'd need to see benchmarks before saying that. The compiler may be able to optimize around call_once properly since I think it's a core intrinsic of FnOnce.

Linear Zoetrope fucked around with this message at 03:24 on Sep 28, 2018

Linear Zoetrope
Nov 28, 2011

A hero must cook

Ralith posted:

Where are you getting this idea that anything is infinite-precision? {integer} is an unknown integer type. It doesn't have magic unique semantics, and it doesn't get coerced because it isn't a specific type to begin with. Arithmetic operators on certain types work in const contexts because they're special-cased to be const fns, and are not special in any other way.

I think I got it confused with Go, which does do infinite precision arithmetic on literals.

Linear Zoetrope
Nov 28, 2011

A hero must cook
Also remember Rust significantly limits the number of usable memory patterns out of the box, so some perfectly valid and safe constructs are disallowed.

Linear Zoetrope
Nov 28, 2011

A hero must cook
I get the same error both ways.

Are you sure you want FnMut and not FnOnce here?

I think the issue you're running into is that FnMut needs to be callable multiple times whereas FnOnce only needs to be called once. You can imagine each having a hidden context or self argument where

FnOnce(self, (other_args)) -> output
FnMut(&mut self, (other_args)) -> output

It sounds like you want the former, since you want it to consume itself.

You could also fix this by cloning from the closure and moving the event into the constructor:

code:
struct Element {
    events: Vec<Box<FnMut() -> Event>>,
}

// If you comment this out and use the add method above, it works.
fn create(event: Event) -> Element {
    let mut el = Element { events: Vec::new() };
    el.events.push(Box::new(move || event.clone()));
    el
}
Which would allow the event to be processed multiple times.

Linear Zoetrope fucked around with this message at 06:10 on Dec 4, 2018

Linear Zoetrope
Nov 28, 2011

A hero must cook
Some of this has resulted in the prelude pattern. For instance, it's pretty common now to do use std::io::prelude::* which is just a submodule the publicly reexports all the common IO traits, and a few libraries that are really trait-heavy have adopted this as well.

Linear Zoetrope
Nov 28, 2011

A hero must cook
Uh... you should be able to depend on yanked crates, right? That's why the whole yanking system exists? Some of my old code broke due to the ncollide package getting yanked and split into two separate crates for 2D and 3D. The crates.io registry errors saying it's not able to find it which is a problem because I'm trying to keep the code as similar is possible (due to study replication guidelines for an academic paper yadda yadda). I upgraded to 0.19 without too much trouble but there's always that bit of nervousness that changing your code changed the dynamics a bit.

I mean, this is academic, nobody is gonna bother replicating it anyway but in principle.

Linear Zoetrope fucked around with this message at 22:25 on May 24, 2019

Linear Zoetrope
Nov 28, 2011

A hero must cook
At least usually when you get terrifying type stuff like that, you can figure out what's going on by looking at the first couple and last couple lines of context.

Unlike macro expansion, debugging macro expansion is hell even with the tools developed for it.

Adbot
ADBOT LOVES YOU

Linear Zoetrope
Nov 28, 2011

A hero must cook
I think the most confusing errors are when you get "the trait does not live long enough". Especially when triggered from a derive proc macro. "The trait does not live long enough" is honestly a really bad error message since it implies that like... Traits can be dynamically added or removed from concrete types (IDK if they've improved the error since I got it). Also unluckily, it's an error that you only tend to get when you're new to Rust, because once you're experienced you've learned the language enough to automatically design in a way that it rarely comes up. (The only time I see it anymore is when wrestling with third party frameworks).

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