6 private links
Shufflecake is a tool for Linux that allows to create multiple hidden volumes on a storage device in such a way that it is very difficult, even under forensic inspection, to prove the existence of such volumes. This is useful for people whose freedom of expression is threatened by repressive authorities or dangerous criminal organizations, in particular: whistleblowers, investigative journalists, and activists for human rights in oppressive regimes. You can consider Shufflecake a "spiritual successor" of tools such as Truecrypt and Veracrypt, but vastly improved: it works natively on Linux, it supports any filesystem of choice, and can manage multiple nested volumes per device, so to make deniability of the existence of these partitions really plausible.
Google web developer page, has documentation on PWA apps
Because everyone likes to use that LendingIterator example I would offer a bit non-trivial example, maybe it would help to show you that GATs lie in the middle between normal generics and higher kinded types which you may have in C++ or Haskell.
The first observation is that many functions make sense and are, actually, implemented for various containers. E.g. map is implemented for Option and map is implemented for Result but they can not be implemented in common trait before GATs because they need to abstract over type of generic: one of them returns Option<U> and the other one returns Result<U>.
What you really want is to use Option or Result as type, generic argument… but you can't. Name of generic type in not a type in Rust!
GATs alllow you to express what you want in a slightly roundabout way.
You can declare trait Mappable like this:
trait Mappable {
type Item;
type Result<U>;
fn map<U, P: FnMut(Self::Item) -> U>(self, f: P) -> Self::Result<U>;
}
And then implement it like this:
impl<T> Mappable for Option<T> {
type Item = T;
type Result<U> = Option<U>;
fn map<U, P: FnMut(Self::Item) -> U>(self, f: P) -> Option<U> {
self.map(f)
}
}
impl<T, E> Mappable for Result<T, E> {
type Item = T;
type Result<U> = Result<U, E>;
fn map<U, P: FnMut(Self::Item) -> U>(self, f: P) -> Result<U, E> {
self.map(f)
}
}
And then, finally, you get the ability to implement function which is generic over generic like this:
fn zero_to_42<C: Mappable<Item = i32>>(c: C) -> <C as Mappable>::Result<i32> {
c.map(|x| if x == 0 { 42 } else { x })
}
This functions kind of “unpacks then type”, works with it's internals and then “packs everything correctly”.
And you can expand it to work with vector, too
It's similar to how you can deal with functions which are generic-over-pointer-types, but kinda more expansive, it should, hopefully, be obvious that GATs cover many more cases than just simple Arc or Rc.
GATs are very useful even without lifetime GATs (in fact lifetime gats are pretty complicated right now).