What's missing from my attempt at a heapless linked list?
My goal is to get the below code to generate the sequence [1, 2, 3] on the stack and then print those values out on separate lines without using Box or anything else requiring the heap or std or malloc.
I've skimmed through https://rust-unofficial.github.io/too-many-lists but all the "good" lists seem to depend on Rc, Box, etc.
The heapless crate is neat but requires knowing the size of a list beforehand.
My Google-fu isn't strong enough to find much help. Any pointers would be much appreciated. But here's what I'm thinking:
struct Node<'a, T> {
value: T,
next: Option<&'a Node<'a, T>>
}
struct List<'a, T> {
head: Option<&'a Node<'a, T>>,
tail: Option<&'a Node<'a, T>>
}
impl<'a, T> List<'a, T> {
fn new() -> Self {
Self {
head: None,
tail: None
}
}
fn push(self, value: T) ->Self {
unimplemented!(); // What's missing here?
}
}
struct Iter<'a, T> {
next: Option<&'a Node<'a, T>>
}
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<&'a T> {
match self.next.take() {
Some(next) => {
self.next = next.next;
Some(&next.value)
},
None => None
}
}
}
impl<'a, T> IntoIterator for List<'a, T> {
type Item = &'a T;
type IntoIter = Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
Iter {
next: self.head
}
}
}
fn main() {
let list = List::new();
let list = list.push(1);
let list = list.push(2);
let list = list.push(3);
for item in list {
println!("{}", item);
}
}
As you can see I'm stuck trying to implement List.push.
Allocating things on the stack without knowing their size (or at the very least an upper bound of their size) is squaring the circle and will not work. You can let the compiler figure out the size for you, but that is pretty much it. The reason for this is simple: Stack allocations may not fail and the compiler has to make sure everything fits in.
If you want to go ahead and stick with the push(T) signature, just taking a value Matt Thomas' answer is the way to go.
Here is my take on the issue, which avoids building nested types:
struct Node<'a, T> {
value: T,
next: Option<&'a Node<'a, T>>,
}
impl<'a, T> Node<'a, T> {
pub fn new(value: T, next: Option<&'a Self>) -> Self {
Node { value, next }
}
pub fn iter(&'a self) -> Iter<'a, T> {
Iter {
current: Some(self),
}
}
}
struct Iter<'a, T> {
current: Option<&'a Node<'a, T>>,
}
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<&'a T> {
match self.current {
Some(Node { value, next }) => {
self.current = *next;
Some(value)
}
None => None,
}
}
}
fn main() {
// Allocation of the Nodes directly on the stack,
// not inside a push method. <= Solves lifetime issues
// Reversed order solves mutability issues.
let three = Node::new(3, None);
let two = Node::new(2, Some(&three));
let one = Node::new(1, Some(&two));
for item in one.iter() {
println!("{}", item)
}
}
Here's a heapless stack that accomplishes the goals stated in the OP:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=fb26b12270bd0a523a693276ec36014f
#[derive(Debug)]
struct Cons<T, U>(T, U);
#[derive(Debug)]
struct MyOption<T>(Option<T>);
trait Push<T>: Sized {
fn push(self, value: T) -> Cons<Self, T>;
}
impl<T, U> Push<U> for Cons<T, U> {
fn push(self, value: U) -> Cons<Self, U> {
Cons(self, value)
}
}
impl<T> Push<T> for T {
fn push(self, value: T) -> Cons<Self, Self> {
Cons(self, value)
}
}
impl<T: Iterator<Item = U>, U> Cons<T, MyOption<U>> {
fn next(&mut self) -> Option<U> {
match (self.1).0.take() {
Some(u) => Some(u),
None => self.0.next()
}
}
}
impl<T> Iterator for Cons<MyOption<T>, MyOption<T>> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
match (self.1).0.take() {
Some(t) => Some(t),
None => (self.0).0.take()
}
}
}
impl<T: Iterator<Item = U>, U> Iterator for Cons<Cons<T, MyOption<U>>, MyOption<U>> {
type Item = U;
fn next(&mut self) -> Option<Self::Item> {
match (self.1).0.take() {
Some(u) => Some(u),
None => self.0.next()
}
}
}
impl<T> Iterator for MyOption<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.0.take()
}
}
fn create_stack() -> impl Iterator<Item = i32> + core::fmt::Debug {
MyOption(Some(0))
.push(MyOption(Some(1)))
.push(MyOption(Some(2)))
.push(MyOption(Some(3)))
.push(MyOption(Some(4)))
}
fn main() {
let stack = create_stack();
println!("Here's the stack:");
println!("{:?}", stack);
println!("Here are the items in reverse order");
for item in stack {
println!("{}", item);
}
}
Output:
Here's the stack:
Cons(Cons(Cons(Cons(MyOption(Some(0)), MyOption(Some(1))), MyOption(Some(2))), MyOption(Some(3))), MyOption(Some(4)))
Here are the items in reverse order
4
3
2
1
0
Caveats
You can't do stack = stack.push(...) in a loop (because stack.push(...) returns a different type)
I didn't think at all about Drop behavior. I guess it would be recursive and blow up for large stacks
This can create huge structs. Try not to move them around too much
Is there a way to create an Iterator that doesn't require the Cons structs to hold Option types? And a way that can be iterated more than once? Maybe
I suspect that every one of those impl functions is duplicated for every element in the resulting stack (since each element is of a different type and all the functions are generic)
Every call to .push() can potentially copy self (not as in the Copy trait, but as in Rust could do a memcpy behind the scenes as part of the ownership move to keep things tidy on the stack)
Related
I'm trying to call SomeClass().call, but am running into compiler errors.
Specifically, running tests for
const std = #import("std");
test "doing a thing" {
{
const calc_result = SomeClass().call(.{});
try std.testing.expectEqual(calc_result, 42);
}
}
fn SomeClass() type {
return struct {
fn call(context: .{}) u32 {
_ = context;
return 42;
}
};
}
results in the error message
src/test.zig:12:17: error: expected type 'type', found '#TypeOf(.{})'
fn call(context: .{}) u32 {
^~~~~~~
referenced by:
test.doing a thing: src/test.zig:5:40
remaining reference traces hidden; use '-freference-trace' to see all reference traces
How do I call a generic type method that takes an empty context?
What you're doing is equivalent to fn foo(value: 0) void {}. Which is obviously wrong. A function definition cannot have values.
You need to define the type of the context:
const std = #import("std");
const Context = struct {
};
fn SomeClass() type {
return struct {
fn call(context: Context) u32 {
_ = context;
return 42;
}
};
}
test "doing a thing" {
{
const calc_result = SomeClass().call(.{});
try std.testing.expectEqual(calc_result, 42);
}
}
Or, use anytype:
fn call(context: anytype) u32 { ... }
void foo<T extends num, String> (T t) {
if (t is String) {
String s = t; // Error
}
}
A value of type 'T' can't be assigned to a variable of type 'String'.
You won't be able to do this with base Dart as your generic type T can only extends one class.
The only way I would see such a behavior feasible would be by using a 3rd party packages such as dartz with its Either type.
Example
void foo<T extends num>(Either<T, String> t) {
final String s;
if (t.isRight()) {
s = (t as Right<T, String>).value;
} else {
s = (t as Left<T, String>).value.toStringAsFixed(3);
}
print(s);
}
foo(Left(1.0)); // prints '1.000'
foo<int>(Right('bar')); // prints 'bar'
There is no syntax to specify that a generic type implement multiple interfaces, so there is no way for this to work with compile-time checks.
Furthermore, your particular example can't work because num and String cannot be extended nor implemented, so it's impossible to have a type that implements both.
If we change your example, which relies on a runtime check, to use two custom types, it still won't work:
class C1 {}
class C2 {
void f() => print('C2.f');
}
class C3 implements C1, C2 {
#override
void f() => print('C3.f');
}
void foo<T extends C1>(T t) {
if (t is C2) {
t.f(); // 'f' isn't defined for the type <unknown>
}
}
See https://github.com/dart-lang/language/issues/2047: t isn't related to C2, so the is C2 check unfortunately will not automatically promote it to C2. You instead can use a runtime cast:
void foo<T extends C1>(T t) {
if (t is C2) {
(t as C2).f();
}
}
or upcast to Object/dynamic first:
void foo<T extends C1>(T t) {
Object t0 = t;
if (t0 is C2) {
t0.f();
}
}
But really you should just use T extends C3 if possible.
I have a requirement to read the memory occupied by an object based on some events. Is there's a simple method that I can use from the standard library ? I need to access the memory usage of the following object which contains nested structs.
HashMap<String, HashMap<AppIdentifier, HashMap<String, u64>>>
I recommend you create a trait with a method that returns the size of self's transitively-owned allocations:
trait AllocatedSize {
fn allocated_size(&self) -> usize;
}
And then implement that for everything involved:
impl AllocatedSize for u64 {
fn allocated_size(&self) -> usize {
0
}
}
impl AllocatedSize for String {
fn allocated_size(&self) -> usize {
self.capacity()
}
}
impl AllocatedSize for AppIdentifier {
fn allocated_size(&self) -> usize {
todo!()
}
}
impl<K: AllocatedSize, V: AllocatedSize> AllocatedSize for HashMap<K, V> {
fn allocated_size(&self) -> usize {
// every element in the map directly owns its key and its value
const ELEMENT_SIZE: usize = std::mem::size_of::<K>() + std::mem::size_of::<V>();
// directly owned allocation
// NB: self.capacity() may be an underestimate, see its docs
// NB: also ignores control bytes, see hashbrown implementation
let directly_owned = self.capacity() * ELEMENT_SIZE;
// transitively owned allocations
let transitively_owned = self
.iter()
.map(|(key, val)| key.allocated_size() + val.allocated_size())
.sum();
directly_owned + transitively_owned
}
}
I need to determine if a generic type is a String, bool, int, double or another class at runtime. I didn't found a way to do it for nullable types:
class Foo<T> {
void foo() {
if (T == int) {
print("'T' is an int");
} else {
print("'T' is not an int");
}
}
}
void main() {
final foo = Foo<int>();
final bar = Foo<int?>();
foo.foo();
bar.foo();
}
console output:
// 'T' is an int
// 'T' is not an int
Is there any syntax I'm unaware of to check for the nullable type?, I've already tried with int? but it doesn't compile.
Here is a more concrete example, based on the approach from How do I check whether a generic type is nullable in Dart NNBD?.
Note that when transpiling to JavaScript, all numbers are IEEE-754 double-precision floating-point values, so to distinguish between Dart double/double? and int/int?, we must first check with a floating-point literal that cannot be an int.
void foo<T>() {
if (1.5 is T) {
if (null is T) {
print('double?');
} else {
print('double');
}
} else if (1 is T) {
if (null is T) {
print('int?');
} else {
print('int');
}
} else {
print('something else');
}
}
void main() {
foo<int?>(); // Prints: int?
foo<int>(); // Prints: int
foo<double?>(); // Prints: double?
foo<double>(); // Prints: double
foo<bool>(); // Prints: something else
}
Note that the above approach won't work for void or Null. Null could be handled by checking T == Null, but T == void isn't valid syntax (similar to T == int?). You can work around that by making them type parameters to a generic function that does the comparison, so another approach is:
/// Returns true if T1 and T2 are identical types.
///
/// This will be false if one type is a derived type of the other.
bool typesEqual<T1, T2>() => T1 == T2;
void foo<T>() {
if (typesEqual<T, void>()) {
print('void');
} else if (typesEqual<T, Null>()) {
print('Null');
} else if (typesEqual<T, int>()) {
print('int');
} else if (typesEqual<T, int?>()) {
print('int?');
} else if (typesEqual<T, double>()) {
print('double');
} else if (typesEqual<T, double?>()) {
print('double?');
} else {
print('something else');
}
}
void main() {
foo<int?>(); // Prints: int?
foo<int>(); // Prints: int
foo<double?>(); // Prints: double?
foo<double>(); // Prints: double
foo<void>(); // Prints: void
foo<Null>(); // Prints: Null
foo<bool>(); // Prints: something else
}
I recommend avoiding using Type objects for anything serious (other than printing or dart:mirrors).
You can create functions to check whether two types, provided as type arguments, are equivalent. Here are some examples:
/// Whether two types are equivalent.
///
/// The types are equivalent if they are mutual subtypes.
bool equivalentTypes<S, T>() {
return _Helper<S Function(S)>() is _Helper<T Function(T)>;
}
class _Helper<T> {}
// Or alternatively:
bool equivalentTypes2<S, T>() {
S func(S value) => value;
return func is T Function(T);
}
/// Whether two types are the same type.
///
/// Uses the same definition as the language specification for when
/// two types are the same.
/// Currently the same as mutual subtyping.
bool sameTypes<S, T>() {
void func<X extends S>() {}
// Spec says this is only true if S and T are "the same type".
return func is void Function<X extends T>();
}
void main() {
print(equivalentTypes<int, int>());
print(equivalentTypes<int?, int?>());
print(equivalentTypes<int?, int>());
print(equivalentTypes2<int, int>());
print(equivalentTypes2<int?, int?>());
print(equivalentTypes2<int?, int>());
print(sameTypes<int, int>());
print(sameTypes<int?, int?>());
print(sameTypes<int?, int>());
}
The language only has one operator for comparing a type to anything, the is operator, which compares an object to a type. That's why all the functions here create an object of a know type depending on S and check it against a type depending on T.
I'm banging my head against this supposedly simple usage of Box whilst trying to create some FFI helper code.
The sample here seems to give an error of free(): invalid pointer when used with a struct that has a field.
pub struct Handle(usize);
impl Handle {
pub fn from<T>(obj: T) -> Self {
let boxed = Box::new(obj);
let mut ptr = Box::into_raw(boxed);
Self::from_ptr_mut(&mut ptr)
}
pub fn from_ptr_mut<T>(ptr: &mut T) -> Self {
Self(ptr as *mut T as usize)
}
pub fn to_box<T>(self) -> Box<T> {
let obj: *mut T = self.to_ptr_mut();
unsafe { Box::from_raw(obj) }
}
pub fn to_ptr_mut<T>(self) -> *mut T {
self.0 as *mut T
}
}
#[allow(dead_code)]
struct Crashes { value: u64 }
impl Drop for Crashes {
fn drop(&mut self) {
println!("Crashes dropped");
}
}
fn crashes() {
let t = Crashes { value: 12 };
let a = Handle::from(t);
let b = a.to_box::<Crashes>();
drop(b);
}
struct Works;
impl Drop for Works {
fn drop(&mut self) {
println!("Works dropped");
}
}
fn works() {
let t = Works;
let a = Handle::from(t);
let b = a.to_box::<Works>();
drop(b);
}
fn main() {
works();
crashes();
}
You can paste this into https://play.rust-lang.org/ and see how it throws aborts with the error free(): invalid pointer
The drop function seems to be called at the appropriate time, but the pointer seems to be somehow invalid
You end up creating a double pointer here:
impl Handle {
pub fn from<T>(obj: T) -> Self {
let boxed = Box::new(obj);
let mut ptr = Box::into_raw(boxed);
Self::from_ptr_mut(&mut ptr)
}
pub fn from_ptr_mut<T>(ptr: &mut T) -> Self {
Self(ptr as *mut T as usize)
}
...
}
Box::into_raw returns a pointer, but then you take a mutable reference to that pointer, and store that address as a usize. You should just be using the *mut T as returned by Box::into_raw.
The reason that the non-working code with the double pointer compiles is that your from<T> and your from_ptr_mut<T> can take entirely different T parameters. If we consider the type T passed to from<T> to be a concrete type, then in this case you're calling from_ptr_mut<U> (where U is *mut T) with an argument of type &mut *mut T.
It should look like so:
impl Handle {
pub fn from<T>(obj: T) -> Self {
let boxed = Box::new(obj);
let ptr = Box::into_raw(boxed);
Self::from_ptr_mut(ptr)
}
pub fn from_ptr_mut<T>(ptr: *mut T) -> Self {
Self(ptr as usize)
}
...
}
Working example in the playground.
Even though we are in the realm of unsafe you can have the compiler do some of the work for you by making the parameter T be bound to your Handle struct. This way you will be statically prevented from loading a different type than was stored.
Playground example where Handle includes a PhantomData.
In this second example you don't have to tell the compiler which item you're retrieving a la a.to_box::<Crashes>(), which is good because you can't introduce undefined behavior by specifying the wrong type.