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
Vanadium
Jan 8, 2005

Hay guys 1.0 is out http://blog.rust-lang.org/2015/05/15/Rust-1.0.html

Adbot
ADBOT LOVES YOU

Vanadium
Jan 8, 2005

Karate Bastard posted:

"Consume"? Really? I see the code they put there, and for what that does, in my experience most people would just say "takes". Sure, I can maybe accept that "consume" could possibly maybe not quite mean what they use it for here, but for most people in programming (?) "consume" has mutable or destructive connotations, like sucking data from a stream for processing, making it inaccessible for other processors of data from the same stream; it is "consumed" like food and cannot be consumed like food again.
You want to look at it from the outside for "consume" to make sense. From the example:
code:
    let mut list = List::new();

    list = list.prepend(1);
    list = list.prepend(2);
    list = list.prepend(3);
The notion that prepend consumes the receiver means that, for example,
code:
list.prepend(1);
list.prepend(2);
list.prepend(3);
would fail to compile because the first prepend call has consumed the list/moved the value out of the list variable, so the variable is consumed, moved-from, dead, until you put a new value in. I guess I'm not saying "consume" is the best word to use but it works for me here.

I can't really argue with the rest of your points. But just to be clear...

quote:

So what does try! do? What does it "return"? What does it "continue"? What does it do that its absence doesn't? Does it explicitly silence errors? If so, why is it called try!?
try! takes a Result<> and either evaluates to the value in the success variant, or it returns the value in the error variant from the function invoking try!, which it gets to do because it's a macro. So on error it returns early, and otherwise it continues in that it doesn't return early, and the code using try! can be written much as if it didn't have to deal with Result<>s as long as it returns the right kind of Result<> itself.

quote:

I'm sort of inquisitive when it comes to new languages and I've given Rust an eye-over a few times in the past, and this sort of thing where the documentation is more of a distraction than a help is not a new thing in Rust, but I must say that it turns me off. I cannot shake the feeling that this weird text is written by a weirdo whose weird ideas also have made it into the language.

What do you guys think? Do you have a similar impression of Rust docs, or is it just me being whiny and/or dumb? And perhaps more importantly, is Rust good? Is it worthwhile learning it (why?) or is it just a weirdo language for weirdos?
I have no idea who wrote the Rust by Example prose and I agree that a lot of the time the documentation is pretty poo poo and hard to understand if you don't already know what's going on, and I can only hope that it gets better now that it's not as much of a moving target. Sometimes the only way to really figure out what a language feature is about is to read the RFC where it was proposed and ideally the pull request where it was implemented, or just asking people on IRC because they're generally helpful (even if some of them are weirdos).

I don't think you should conclude that the language is equally weird and haphazard, there's a lot more eyes on language features than on some quasi-third-part code example project and the people in charge of the compiler internals aren't the ones writing the docs in the first place (unfortunately?).

I think it's a good language, you might not get much out of learning it if you aren't enthusiastic about writing code in no-GC-allowed environments. It does have its weird spots and the restrictiveness of the borrow checker is really frustrating, so ymmv. The good parts are pretty much variants+pattern matching and decent abstractions for low-level-ish stuff, but it's nothing that's gonna ~blow your mind~ like Haskell or whatever.

Vanadium
Jan 8, 2005

Rust is taking heavy criticism on twitter for posting a "team" page that is apparently all dudes, mostly white, and one of the people organizing the Berlin usergroup is super frustrated now because all the negative publicity is making their job harder. :(

Vanadium
Jan 8, 2005

Like there isn't plenty of nerds who'd look at an open source project 100% run by women and go "lol i'm not getting near that, this is clearly for girls, i'm not a girl". I don't think it's too farfetched that an all-guys lineup is offputting to women, which obviously isn't ideal for adoption of the language.

In an ideal world the gender of the people organizing your open source community wouldn't matter. In this fallen world we live in, even the most well-meaning guys tend to turn out to be total goons and antagonize women for no good reason if left to their own devices.

Vanadium
Jan 8, 2005

strcat's technical arguments seemed to take the shape of "here's the right way to do it, i don't see why you'd possibly want to do anything else, clearly you aren't really invested in making rust a good language" every time, I can see how people got pissed off at that. I mean he was still right a lot of the time, but somehow that didn't help his case after a while.

Vanadium
Jan 8, 2005

Do they have a commonly used quickcheck yet?

Vanadium
Jan 8, 2005

Jsor posted:

Also, I think the more "Rustic" way is to use an enumerated type instead of a faux-null-pointer option

code:
enum BSTNode {
    Node{left: Box<BSTNode>, right: Box<BSTNode>, value: i64},
    Sentinel,
}
So instead of having the option to have a left or right pointer, you always point to a node which is either a full node or an end marker. So an empty tree is simply a single Sentinel.

I don't think there's anything wrong with Option<Box<...>>. It's fairly common and compiles down to a potentially-null pointer, and it saves you the allocations.

Sweeper posted:

I'm basically know nothing about rust, but is there any way to "hide" mutability of a struct?

[...]

How do I not pass '&mut self' to the add function, and keep it as &self? I don't want to declare every object as mutable because a map it contains is mutable. I'd like to hide that implementation detail from the consumer...

Also is there any long winded explanation of lifetimes, I'm an idiot and need more words

Jabor posted:

If you're doing something that modifies your object, you're going to need it to be mutable. This is by design. Having a function that modifies your object out from underneath you despite only taking a read borrow would defeat the entire purpose of the borrow checking and blow all of those safety guarantees out of the water.

Whether a function changes an object isn't an "implementation detail" to be hidden away, it's an inherent part of the contract of that function.

There's a lot of head-wrapping-around to be done with Rust in terms of how it straight-up doesn't let you structure some programs like it'd be the right way in millions of other languages. People on irc tend to scold me when I'm all "but that makes everything ugly and terrible and now I need to pass my state around explicitly like some sort of caveperson", and somehow it never seems to be a problem for all those clever things people do in Rust but I don't really get it.

That said you can use RefCell to move the safety guarantees to dynamic checks instead, that should work here.


rrrrrrrrrrrt posted:

What the heck is the difference? Would you ever actually need more than one explicit lifetime for a struct?

I think in both cases the struct itself is effectively restricted to the intersection of the two lifetimes, and I think it's fairly uncommon to have two lifetime parameters on a single type. But if you want to get one of the references out of the struct again, there's a difference in whether you've coerced both of them to the same intersectional lifetime. Your example isn't too helpful there because your string literals have static lifetime either way, so let's assume a Butt with two integer reference fields instead, then the following would only work with the Butt with separate lifetimes:

code:
fn main() {
    let dilz = 32;
    let p = {
        let dong = 42;

        let rear end = Butt{fart: &dilz, poop: &dong};

        rear end.fart
    };

    println!("My dilz: {:?}", *p);
}

Vanadium fucked around with this message at 09:16 on Jun 20, 2015

Vanadium
Jan 8, 2005

quote:

Rust 1.1 stable provides a 32% improvement in compilation time over Rust 1.0 (as measured by bootstrapping).

quote:

Benchmark compilations are showing an additional 30% improvement from 1.1 stable to 1.2 beta

I was fairly excited about this like an hour and a half ago when I started building rust from source, not so much now :(

Vanadium
Jan 8, 2005

It's a catch expression thank you very much :colbert:

Vanadium
Jan 8, 2005

Generally: Getting more errors when you fix one error is perfectly normal and just means the compiler now understands enough of your code to be really unhappy about it. Being unable to find methods you know exist probably has to do with the trait bounds of the impl where the methods are defined not being fulfilled. The type-must-be-known thing is a what you usually get when you try to make it do more inference than it reasonably can, or I guess when it tries to figure out how to continue with code that's already erroneous.

It looks like you need to do input_image.get_pixel(x, y).to_rgb(); before you can get at the data field, because the data field is a thing defined on the concrete Rgb type and not on the Pixel trait (traits don't define fields), but it also looks like you could just call .to_luma() instead and skip the manual averaging and also get some weird weighting they do for each channel.

Vanadium
Jan 8, 2005

Don't think there's a great way. In C you'd just take an out-pointer and then just return a flag, but that's not gonna look particularly nice in either Rust or C#.

Vanadium
Jan 8, 2005

Edit: The box doesn't get dropped because transmute takes it by value, and then internally forgets it.

deregister_voxel is a reference to a closure that closes over the deregister_voxel_extern pointer on the stack, borrowing it. So the closure reference and also the closure itself are basically broken once you return from svo_create. I assume you hide the lifetime problems from the borrow checker by transmuting to something with 'static lifetime.

If you want to use closures here, they should capture all their state by move (move |foo| { ... }), and then you probably gotta box them into a Box<Fn(...) -> ...>. Alternatively maybe you can store all the relevant state in that struct you return, including those function pointers you take, and just impl methods on the struct instead of putting closures in? I dunno.

I'm happy to see Rust chatter somewhere on CoC even if I can't answer questions very well. :shobon:

Vanadium
Jan 8, 2005

I'm surprised that the Fn traits aren't implemented or non-Rust ABI fns, but it looks like that's it.

The missing -> u32 is baffling, that frankly looks like a bug in the error message. Edit: Dude says it's because it's an associated type. Ugh.

(fwiw people tend to pass closures by value when possible and I don't recall having seen &|...|)

Vanadium fucked around with this message at 00:36 on Mar 28, 2016

Vanadium
Jan 8, 2005

Jsor posted:

1. Why does Fn require FnMut require FnOnce? Having FnOnce be the base type almost seems backwards to me, especially since this is roughly akin to Take By Immutable Reference -> Take By Mutable Reference -> Take By Move. It feels like taking by reference should be the "least onerous" to implement. But regardless it feels like all these traits should be disjoint, rather than extensions of each other. (Not that you generally implement the Fn traits yourself anyway).

I dunno, this makes sense to me? FnOnce leaves the most freedom to the implementer (can consume environment), FnMut slightly less (can only mutate, not consume) and Fn the least (can only look, not touch). If you have a function that can work by immutable reference, it'll also work if you let it mutate the environment, but not the other way around, so FnMut requiring Fn would make zero sense because then a function can only mutate its environment if it can also work without mutating it.

I'm not sure about the Sync/Send thing, I always get confused there.

Vanadium
Jan 8, 2005

Jo posted:

I'm bumping my head against a module error. Most of the people online seem to say "modules are confusing" and I agree with that sentiment.

I have the following structure:

code:
\ <myapp>
 | Cargo.toml
 \  <src>
   | app.rs
   | geometry.rs
   | settings.rs
   | main.rs

For that to work out you want no mod lines anywhere but main.rs, main.rs has mod app; mod geometry; mod settings;, and everybody else has use app; use geometry; use settings; as necessary.

mod items are for defining your crate's tree structure, you pretty much only ever want one mod line per module in your whole crate, probably at the highest level at which you want that module to be used. use items just bring stuff into scope so you don't have to use absolute paths that start with :: everywhere.

Vanadium fucked around with this message at 01:03 on May 17, 2016

Vanadium
Jan 8, 2005

Yeah you can't really name closure types so you can't reasonably return them out of generic code, so you always have to be generic over them.

Fundamentally, fn foo<T: Fn()>() -> F can't possibly work because foo promises to be able to return a closure of any type of the caller's choosing, which it obviously can't. The inverse where foo returns a closure of a specific secret type that the caller just has to deal with can't currently be expressed, except with boxing+type erasure it like you're doing.

Vanadium
Jan 8, 2005

Result<(), T> is a legit pattern. :colbert:

Vanadium
Jan 8, 2005

QuantumNinja posted:

You see it in std::fmt and the serialzied, and a few other places, but it's never carried very far in those APIs. taqueso's snippet sort of implies that it's going to carry a Result<(), T> through a few layers to "propagate the error", though, and that's going to induce a nasty ok_or chain all the way up the callstack.

Yeah that's what try!() is designed around! Putting error results in the Err variant of Result!

Vanadium
Jan 8, 2005

Day 1 of rustfest was pretty cool but I'm too lazy to go back to day 2 for the workshops, rip.

Vanadium
Jan 8, 2005

The ? thing is pretty cool and in general I'm a fan of the explicit Result<> returning, but actually creating a ~good~ error enum or w/e still seems like a lot of pain. Maybe I should just use Strings as errors.

Vanadium
Jan 8, 2005

Do you wanna post your code? I mean I've looked at a few of that sorta crate and I still end up getting a bit of a headache each time I think about what type of error I should return and how to structure them.

Vanadium
Jan 8, 2005

I love that post, brson owns.

Vanadium
Jan 8, 2005

Yeah, the assignment never happens. It's just that a lot of things in Rust are expressions, so you're more flexibly with what goes where than in C or whatever. You can also write stupid poo poo like return (return (return (return continue))) because continue and return are just divergent expressions of type !. I don't think patterns are relevant at this point.

It's like an .unwrap_or_else(|| continue) if you could magically use continue across closures.

You can also write stuff like this:
code:
let foo = {
  let x = do_a_bunch_of_stuff();
  struct MaybeEvenDeclareALocalType {...}
  ...;

  ultimately_just_put_the_value_for_foo_here(x)
};
and it'll do all the stuff in the block before assigning the result of the final expression to foo. giving you opportunity to return or panic!() or anything.

Vanadium
Jan 8, 2005

Ideally you just use the Entry based API for you lookup-or-insert thing, but I guess they're still arguing about how to design the API that doesn't require an up-front copy of the key.

Lifetimes extending past where you'd except them to is a thing they've been posting about for ages, Niko Matsakis has been blogging about it recently so maybe things are happening there, search for non-lexical lifetimes. The if-let thing might be a bug that's fixed in nightly or something.

Vanadium
Jan 8, 2005

https://github.com/rust-lang/rfcs/pull/1769 sometimes rust just feels like complete bullshit.

Vanadium
Jan 8, 2005

I think in this case it doesn't matter that it's in the heap, just that you have references with a lifetime bound to a local in the closure, so they aren't allowed to escape the closure.

Vanadium
Jan 8, 2005

That sounds like it's unlikely to work, I don't think 'a is the right lifetime here. You need to arrange for the body to stick around for the lifetime parameter to Deserialize, so if that should be 'a then you'd probably need to stash the body in the Fetcher struct, which sounds like somewhat wonky semantics for a Fetcher.

Maybe it works if you ask the caller for a &mut String to read into and use the lifetime of that for Deserialize?

Disclaimer: I've only used serde with some extremely mechanical Serialize/Deserialize implementations, I might be missing out on the subtleties.

Vanadium
Jan 8, 2005

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2015&gist=a31af09574565b12582fdd6477e32d44 seems to work modulo playground sandboxing:

code:
    pub fn fetch2<'b, T: Deserialize<'b>>(&self, url: &str, body: &'b mut String) -> Result<T, Box<std::error::Error>> {...}
...
    let mut b = String::new();
    f.fetch2(..., &mut b).unwrap();
But to me it sounds like DeserializeOwned makes sense as the bound here.

I got a bit upset that I couldn't figure out how to avoid needing this weird DeserializeOwned trait that I'd never heard of before, but I realized it's implemented for for<'t> Deserialize<'t> and you can basically inline that into your definition if you wanted:

code:
    pub fn fetch<T: for<'t> Deserialize<'t>>(&self, url: &str) -> Result<T, Box<std::error::Error>> {

Vanadium
Jan 8, 2005

afaik you're supposed to be able to keep using a Cargo.lock that already has those yanked revisions baked into it, but you won't be able to get there anymore from just a Cargo.toml.

Either way that that seems like a weird reason to yank a crate.

For replication, maybe you could find the right git revision corresponding to the yanked release and depending on that directly instead of going through cargo.

Vanadium
Jan 8, 2005

gonadic io posted:

That's what I tried but the future stuff requires the closure to be static too so it's a bit awkward. Maybe you need to find the place between being inside the FnMut and being inside the future. I couldn't quite get it working. There's also two FnMuts which isn't helping things.

It's just https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=73c14a8e6fdb3b3795746f544adc39b7 right?

Rust code:
    let secret_map: SecretMap = Arc::new(Mutex::new(HashMap::new()));

    let make_svc = make_service_fn(move |_conn| {
        let secret_map = secret_map.clone();
        async {
            Ok::<_, Infallible>(service_fn(move |req| handle_request(req, secret_map.clone())))
        }
    });
The hyper server pattern is awkward.

Vanadium
Jan 8, 2005

anyhow has a feature flag for backtraces, doesn't it?

Vanadium
Jan 8, 2005

You shouldn't need that mod item, you should be able to refer to the lib module by yourcratenamehere::lib by default. Maybe that makes things better?

If you can't post the whole thing to get help, maybe you can recreate the problem in a minimal example repo? It doesn't sound like there are too many moving parts, except maybe a sample external C library.

Vanadium fucked around with this message at 20:17 on Jul 16, 2022

Vanadium
Jan 8, 2005

A package can have multiple binary crates in it but only one library crate, and idk I guess [[bin]] is toml for I'm just one entry in a list of potentially many.

fwiw something about the module system trips me up every time. I don't wanna say it's bad or even less intuitive than other languages, it just makes me go "... huh." surprisingly often.

Edit: And yeah, I still (without evidence/authoritative amounts of experience/having tried it) is that your problem is that the binary crate doesn't use the library crate, but instead incorporates its source independently.

Vanadium fucked around with this message at 04:43 on Jul 20, 2022

Vanadium
Jan 8, 2005

Rocko Bonaparte posted:

I blew quite a bit of time trying to invoke BufReader::lines() today. It was because I had neglected to import std::io::BufRead. I don't use BufRead directly but I guess that adds appropriate traits to use BufReader::lines on files. I got lucky with IRC and cross-checking examples online in this case, but how could I anticipate I'm neglecting something like that in the future?

Doesn't the compiler error tell you to do that?

Edit: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1ac677079f88616b65c7d97c7ff40292

code:
help: items from traits can only be used if the trait is in scope help: the following trait is implemented but not in scope; perhaps add a `use` for it:

use std::io::BufRead;

Vanadium fucked around with this message at 10:19 on Aug 3, 2022

Vanadium
Jan 8, 2005

I've seen people get confused by diagnostics when they were only looking at what rust-analyzer says about an individual line instead of the full rustc output before, there's clearly some suboptimal presentation there. :shobon:

Vanadium
Jan 8, 2005

Ad a compromise, maybe don't handle certs in-process and use a proxy/load balancer tier for TLs termination, but also write the proxy in rust.

Adbot
ADBOT LOVES YOU

Vanadium
Jan 8, 2005

It sounds like the core motivation is being able to dunk on people who try to sell lovely off-brand compilers introducing incompatibilities, but they haven't figured out how to get the lawyers to write a policy that a) achieves that b) doesn't piss off the kind of person who lives in an open source community, and I guess it's unclear if that's possible.

Not letting local user groups or meetups use rust in the name seems particularly annoying compared to what other projects do (tho i get protecting rustcon specifically), and also not allowing rust in crate names. I do enjoy that they claim the trademark for clippy at least.

I'm pretty sure at least as it stands if you email the people working on the trademark thing with your proposed not-evidently-hostile use they're just going to be like "yeah that sounds cool go for it", but I get people being nervous about long term.

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