Earlier quoted context omitted.
"You have undefined new messages" Glorious!
It's very easy to spot the error here, compared to for example a segfault.
Rust and the Future of Systems Programming [video]
431–440 of 511 posts
Re: Rust and the Future of Systems Programming [video]
#432Earlier quoted context omitted.
So, since we're talking about Rust... print!("You have %d new messages", messages) Was that supposed to make me want to abandon all type safety and embrace a GC'd language that runs in a VM?
There are advantages to GC, even Rust has runtime GC. But where is the memory freed ? You don't have to think of that at all in JavaScript. Also when something goes wrong with types in JavaScript the worst case scenario is "2"+2 turns into "22" witch is easy to avoid, compared to a silent overwrite/overflow. Even if the types in JavaScript is very loose, they are much safer.
If you want GC there are any number of good languages to pick with it (e.g. OCaml).
> Also when something goes wrong with types in JavaScript the worst case scenario is "2"+2 turns into "22" witch is easy to avoid, compared to a silent overwrite/overflow.
It's memory-safe but it's not safe. What if an error like that happens in your permission-checking code? I agree that silent overwrites and silent overflows should not happen and that language that have those things are bad, but that doesn't make silent type errors any better.
Re: Rust and the Future of Systems Programming [video]
#433Earlier quoted context omitted.
> References are one of the unsafe C++ elements that SaferCPlusPlus is intended to be used to replace [1]. OK, so you can't use references. Then, as I said before, your pointer replacements have a runtime performance cost worse than GC write barriers. > Yes, that series of operations is safe. A related example from the "msetl_example.cpp" file: I don't think you understood me. I mean the this pointer. "this" is hardw…
> OK, so you can't use references. Then, as I said before, your pointer replacements have a runtime performance cost worse than GC write barriers. The library provides three types of pointers - "registered", "scope" and "refcounting". I believe you are referring to the registered pointers, that indeed have significant cost on construction, destruction and assignment. But registered pointers are really mostly intended…
The magic all lies in the compiletime borrow checker, which roughly works like this:
- All data is accessed either through something on the stack or in static memory.
- Accessing data, say by creating a reference to it,
causes the compiler to "borrow" the value for the scope in which the reference
is alive.
- The references can be alive for any scope equal or smaller than for which
access to the data itself is valid.
- References track the original scope for which they are alive around as a
template-paramter-like thing called "lifetime parameter".
Note that Rusts use of the word "lifetime" is thus a bit narrower than the
one used in C++, since it just talks about stack scopes, and not the lifetime
of the actual value as would be tracked by a GC or ref counting.
Example:
let x = true;
let r = &x;
Here, r would infer to a type like `Reference`.
(The actual type in rust would be a `&'a T` with
'a = scope of x, and T = bool).
- Because the scope is tracked as part of the reference type,
it is possible to copy/move/transform/wrap references safely, since
the compiler will always "know" about the original scope and thus can
check that you never end up in a situation where you accidentally outlive the
thing you borrowed, say if you try to return a type that contains a reference
somewhere deep down.
- The borrow itself acts as a compiletime read/write lock on the thing you referenced,
so for the scope that the reference is alive for the compiler prevents
you from changing or destroying the referenced thing. Example:
// This errors:
let mut a = 5;
let b = &a;
a = 10; // ERROR: a is borrowed
println!("{}", *b);
// This is fine:
let mut c = 100;
{
let d = &c;
println!("{}", *d);
}
c = 50;
- The above examples just use `&` for references, but Rust has two references types:
- &'a T, called "shared reference", which cause "shared borrows".
- &'a mut T, called "mutable references", which cause "mutable borrows".
- Both behave the same in principle, but have different restrictions and guarantees:
- A mutable borrow is exclusive, meaning no other other borrow to the same data
is allowed while the &mut T is alive, but allows you to freely change the T through
the reference.
- A shared borrow may alias, so you can have multiple &T pointing
to the same data at the same time, but you are not allowed to freely change T through
the reference.
- (If those two cases are too rigid there is also a escape hatch that
a specific type may opt-into to allow mutation of itself through a shared reference, with
exclusivity checked through some other mechanism like runtime borrow counting.)
- Through these two reference types, Rust libraries can abstract with arbitrary APIs
without loosing the borrow checker guarantees. Eg, the "reference to vector element"
example boils down as this:
let mut v = Vec::new();
v.push(1);
let r = &v[0]; // the reference in r now has a shared borrow on v.
v.push(2); // push tries to create a mutable borrow of v, which conflicts with the
borrow kept alive by r, so you get a borrow error at compiletime.
println!("{}", *r);
The important part is that all this is there, per default, for all Rust code in existence, so you can not accidentally ignore it like a library solution you might not know about, or like language features that don't know about the library solutions.Re: Rust and the Future of Systems Programming [video]
#434Earlier quoted context omitted.
This is not true. For example, consider this code: if foo.is_some() { let foo = foo.unwrap(); } else { // other code } Here, I _know_ that foo is some. The extra error message from expect will _never_ be seen. Now, this is a contrived example, and would better be written with `if let` in today's Rust, but this is the _kind_ of situation in which unwrap is totally, 100% cool, but the compiler can't know.
> Now, this is a contrived example, and would better be written with `if let` in today's Rust, but this is the _kind_ of situation in which unwrap is totally, 100% cool, but the compiler can't know. If the author knows it's safe, they should be able to express how they know in a way that the compiler can understand. Certainly I think there's a large space of use cases where the extra guarantee provided by forbidding…
Re: Rust and the Future of Systems Programming [video]
#435Anyone know a tutorial course for someone only knowing high level languages. And not c or Ruby. More JavaScript, PHP, nodejs, Python
Re: Rust and the Future of Systems Programming [video]
#436Earlier quoted context omitted.
I think I know what this trend is, more generally. It has to do with how explicit our code is. We've been through an era where you have some very powerful and compact indirection constructs(event callbacks, polymorphic objects, dynamic types, exceptions) in common parlance and the trend has turned against these lately. Their utility in many instances is mostly to enable technical debt, by worrying about the edge case…
Go is more verbose because it lacks a type system that allows generic programming, that seems like a very different kind of verbosity, than explicit error handling IMO. Correct me if I'm wrong, but I don't believe Go's type system enforces you check and handle errors either, so it's not really enforcing verbosity where it counts.
Go's type system doesn't, but the compiler will in a good number of cases. For example, this will produce a compile time error:
value, err := Foo()
fmt.Println(value)
It produces an error because `err` was declared and unused.The following defeats this check though:
value, _ := Foo()
// Bar returns one value: an error
_ = Bar()
Bar()
The above list probably isn't exhaustive.Re: Rust and the Future of Systems Programming [video]
#437Earlier quoted context omitted.
It's very easy to spot the error here, compared to for example a segfault.
That's damning with faint praise if I ever heard it. (And it's not even true; undefined tends to propagate further, the segfault usually happens closer to the actual error. Not that I'm defending languages that segfault by any means)
Re: Rust and the Future of Systems Programming [video]
#438Earlier quoted context omitted.
If your only error handling mechanism is exceptions and you disable exceptions because you can't bare the cost, then what are you left with? > The people who can't tolerate exceptions are the same ones who want precise machine control and who probably don't want stdlib either. I don't agree. Just because I don't want to pay for exceptions doesn't mean I don't want, say, convenient platform abstractions over file syst…
What is reporting the errors? In a standalone environment, in which you're left with the core language, anything that reports errors is something you can define, and you can define that component to use error codes, just as we would in C. It feels odd to want exact control over the error handling abstraction but want to use Rust's convenient IO abstraction. Performance either matters or it doesn't. > debate unto itse…
I don't think it comes from Go; rather it comes from the ML family (which Rust is arguably a member of). ML has had exceptions and error codes for decades, and ultimately that experience has come down on the side of error codes, because with good sum types and higher-order functions they are safer and more effective than exceptions. An analogy: early fighter planes were aerodynamically unstable, as it was hard to make them maneuverable any other way. Later fighter planes were stable as this was safer and easier to control. Modern fighter planes are aerodynamically unstable, as modern control systems can control such planes effectively and the original advantages remain.
Re: Rust and the Future of Systems Programming [video]
#439Earlier quoted context omitted.
> Being able to panic on OOM won't go back in time and rewrite stdlib I don't see how that's relevant? If you have to deal with OOM you're probably not going to deal with it at a fine-grained level, you'll have one high-level panic catcher somewhere that handles this and all other panics. Given that overcommit exists as well, this makes the cases where you want workable Result-on-OOM quite niche. (And there is work -…
Hundreds of millions of people use non-overcommit systems. That's a good thing, because overcommit is a mistake that encourages profligate use of system resources. I fear that abort-on-OOM will only reinforce the presumption of overcommit in the minds of developers. Even on overcommit systems, you can run out of address space or vsize. I believe in treating memory like any other resource. You wouldn't abort by defaul…
If most of the language and standard library required allocating disk space to function then I would indeed abort by default, because very few programs would be able to do anything useful in those conditions, so the most useful thing is to fail fast.
It's possible to design a language and standard library that can remain usable in out-of-memory conditions, but the costs would be severe, and not justified for the overwhelming majority of rust use cases, I think.
Re: Rust and the Future of Systems Programming [video]
#440Earlier quoted context omitted.
> Your runtime simply needs to bound the amount of reclamation work done at any given time. Wouldn't this transform the problem into a "no more predictable maximum memory usage" problem? As you can't really know if and when your GC will keep up it with the amount of work to do.
Possibly, but maximum memory usage is rarely predictable anyway. I expect it might be even less predictable than maximum latency. However, it may still be possible to conservatively bound your maximum memory usage too, as long as your reclamation-work phase keeps up with your program's allocation rate, then you achieve a steady-state. Suppose some amount of reclamation is done on malloc(), a tunable parameter could m…
Well, if you don't need a bound on memory usage you can just never deallocate.
> Suppose some amount of reclamation is done on malloc(), a tunable parameter could measure the ratio of allocation speed of the running program and amount of unreclaimed garbage. This ratio would control how much reclamation work to do before returning from malloc() so you can fall into steady-state.
Sure, but that doesn't guarantee anything about what your maximum spikes are going to be. You can have a firm bound on memory consumption or a firm bound on latency, but you can't get both without doing some serious application-specific work.