With Nimony defaulting to atomicArc, will that give reliable performance for real time systems?
I think so. I figured out how to improve the optimizer even further so that the overhead should be perfectly fine. If not, we'll offer a switch to get non-atomic ref.
how will the FFI be affected with the incremental compilation
Both the FFI and IC are already working well and for the end user there is no interaction between these two. Though maybe I don't understand your question, what problems do you think there are?
will the windows target be a priority? Windows is as as important as Linux and all of our testing is also done on Windows.
If not, we'll offer a switch to get non-atomic ref.
All that's needed really is a unique ref that disconnects "sharing things mutably" from "always copying all the big things" - with a unique ref, 90% of the reasons to use ref as it exists today go away and nobody will notice or care if ref atomic or not because at the end of the day because it won't see much use.
Global switches are still completely useless - you can't write a library for people to reuse with global switches - it has to be controlled at a local level - ie whether you want atomic ref counting or not is not a question to answer globally - instead, there are a few sweetspots where it can be useful and a whole ocean of code where it's useless overhead.
You don't need shared mutable atomic-counted access in most cases - specially not in multi-threaded code that instead thrives on shared read-only access and division of mutability (to avoid contention while not using up too much memory). In most code, you just want to be able to move things around cheaply without blowing the stack.
Well I spent months on getting owned vs unowned ref to work conveniently so I beg to differ.
Sure, C++ gets by with unique_ptr but then C++ doesn't claim to be memory safe so that references (and plain pointers) to unique_ptr can be formed nilly willy, so that link traversal remains possible.
Rust also offers it and it's memory safe but it also doesn't work well:
#[derive(Debug)]
enum BinaryTree<T> {
Empty,
Node {
value: T,
left: Box<BinaryTree<T>>,
right: Box<BinaryTree<T>>,
},
}
pub fn search(root: &BinaryTree<i32>, target: &i32) -> Option<Box<BinaryTree<i32>>> {
let mut current = root;
loop {
match current {
BinaryTree::Empty => return None,
BinaryTree::Node { value, left, right } => {
if target == value {
return Some(Box::new(*current));
// ^ what to do here?
} else if target < value {
current = left;
} else {
current = right;
}
}
}
}
}
Swift is memory safe and offers atomic refcounting by default and hardly any unique pointers. This is not a coincidence.
Global switches are still completely useless - you can't write a library for people ... That is an over-generalization. First, not all software is library-upon-library fragile slop. Second, I don't think it's a good idea to force everyone to e.g. atomic refs just because someone wrote a library that works only with atomic refs.
All that's needed really is a unique ref If you want unique ownership of some resources, why not make a move-only type that owns them? Why choose the awkward C++ middle ground of still exposing the pointer but kinda making it safe?
not all software is library-upon-library fragile slop
Not entirely sure what you mean by "fragile slop", but most software ends up using at least several libraries - the std lib - which also have to cater to the exponential number of combinations that you get from global options.
BinaryTree<T>
Returning a borrowed reference to the entry with a lifetime that extends beyond the "main" tree seems reasonable in this case? Either that, or you have to copy it since .. it's single-owner.
#[derive(Debug)]
enum BinaryTree<T> {
Empty,
Node {
value: T,
left: Box<BinaryTree<T>>,
right: Box<BinaryTree<T>>,
},
}
pub fn search<'a>(root: &'a BinaryTree<i32>, target: &'a i32) -> Option<&'a BinaryTree<i32>> {
let mut current = root;
loop {
match current {
BinaryTree::Empty => return None,
BinaryTree::Node { value, left, right } => {
if target == value {
return Some(current);
// ^ what to do here?
} else if target < value {
current = left;
} else {
current = right;
}
}
}
}
}
why not make a move-only type that owns them?
To be able to "upgrade" a single-owner ref to a shared ref and to have the language rules that apply to ref to seamlessly interact with the single-owner ref as well.
You can also interpret this as a point that the compiler special-cases ref in many of its internals and these special-casings would first have to be exposed as general-purpose features and traits so that a library-based unique ref can be as convenient as a ref to use. I would love for this to happen.
Finally, there are quite a few bugs with move-only types and how they interact with other language features, so more practically, this is simply not possible with the nim that we have today.
Well I spent months on getting owned vs unowned ref to work conveniently so I beg to differ.
I've always wondered why that fizzled - it was certainly an interesting direction and presumably it hit some roadblock that perhaps can be revisited now?
Either that, or you have to copy it since .. it's single-owner.
That's the point though. You cannot copy it, it contains Box'ed pointers.
Returning a borrowed reference to the entry with a lifetime that extends beyond the "main" tree seems reasonable in this case?
Not sure I understand how unique pointer helps here. If the reference can outlive the tree, you either need static lifetime analysis as in your snippet, or reference counting. If it can't, you just need a normal pointer.
To be able to "upgrade" a single-owner ref to a shared ref and to have the language rules that apply to ref to seamlessly interact with the single-owner ref as well.
Yeah, I'm not sure this is something good. So far the code I've seen (or written) that has a mix of unique_ptr<T> and shared_ptr<T> for the same T was a mess. It was usually the case that T was conceived in isolation, without understanding how it interacts with the rest of the program. I'm sure there are niche cases where it works well, but in niche cases you can just create two types for shared and unique ownership.
Not entirely sure what you mean by "fragile slop", but most software ends up using at least several libraries - the std lib - which also have to cater to the exponential number of combinations that you get from global options.
What I have in mind is javascript and (to lesser extent) python, where to do something trivial you end up pulling tens and hundreds of packages. Of course, in such setting any global switch that is set to a non-default value almost surely will break something.
If you try to minimize the number of libraries, and the libraries you use are focused and limited in scope, you just don't get the "cater to the exponential number of combinations" problem. But I fully share the sentiment that global switches is something to be avoided if possible.
That's the point though. You cannot copy it, it contains Box'ed pointers.
That's the point, you don't want a copy - you want a reference? Ie should it copy the whole subtree? That's a deep copy, kind of defeats the purpose, but sure, it's a clone then:
#[derive(Debug, Clone)]
enum BinaryTree<T> {
Empty,
Node {
value: T,
left: Box<BinaryTree<T>>,
right: Box<BinaryTree<T>>,
},
}
pub fn search(root: &BinaryTree<i32>, target: &i32) -> Option<Box<BinaryTree<i32>>> {
let mut current = root;
loop {
match current {
BinaryTree::Empty => return None,
BinaryTree::Node { value, left, right } => {
if target == value {
return Some(Box::new(current.clone()));
// ^ a clone
} else if target < value {
current = left;
} else {
current = right;
}
}
}
}
}
The lifetime analysis is what allows you to safely return a non-owning "view" reference without sacrificing ownership. Perhaps this is the critical point here, that when we talk about single-owner, we still allow views into the data by reference which is what makes "search" practically implementable.