Here is an example of some structs:
enum VehicleType {
Car,
Motorcycle,
}
struct Vehicle {
name: String,
horsepowers: i32,
vehicle_type: VehicleType,
}
struct Person<'a> {
vehicle: &'a Vehicle,
name: &'a str,
}
and in main function:
let mut car = Vehicle {
name: "Nissan GTR".to_string(),
horsepowers: 300,
vehicle_type: VehicleType::Car,
};
let alice = Person {
name: "Alice",
vehicle: &car, // Share a reference to the same car
};
let bob = Person {
name: "Bob",
vehicle: &car, // Share a reference to the same car
};
println!("{} drives {}", alice.name, alice.vehicle.name);
println!("{} drives {}", bob.name, bob.vehicle.name);
Now let's say we want to update the name of the car while preserving that Alice and Bob drive the same car
car.name = "Lamborghini".to_string();
car.horsepowers = 684;
println!("{} drives {}", alice.name, alice.vehicle.name);
println!("{} drives {}", bob.name, bob.vehicle.name);
This of course fails because car is borrowed by both Alice and Bob.
Why wouldn't rust compiler allow this? How does this introduce memory safety issues? How to go about a pattern like this?
You cannot mutate an object when it is immutably borrowed.
If you want to reference the same Vehicle object in the Person structs and be able to mutate it, you may want to use a reference to a RefCell<T>:
use std::cell::RefCell;
enum VehicleType {
Car,
Motorcycle,
}
struct Vehicle {
name: String,
horsepowers: i32,
vehicle_type: VehicleType,
}
struct Person<'a> {
vehicle: &'a RefCell<Vehicle>,
name: &'a str,
}
fn main() {
let mut car = RefCell::new(Vehicle {
name: "Nissan GTR".to_string(),
horsepowers: 300,
vehicle_type: VehicleType::Car,
});
let alice = Person {
name: "Alice",
vehicle: &car,
};
let bob = Person {
name: "Bob",
vehicle: &car,
};
println!("{} drives {}", alice.name, alice.vehicle.borrow().name);
println!("{} drives {}", bob.name, bob.vehicle.borrow().name);
car.borrow_mut().name = "Lamborghini".to_string();
car.borrow_mut().horsepowers = 684;
println!("{} drives {}", alice.name, alice.vehicle.borrow().name);
println!("{} drives {}", bob.name, bob.vehicle.borrow().name);
}
NB: i32 is a poor choice for the horsepower of the car, since a negative horsepower does not make any sense. Consider using u32 instead.
Related
I'm using the Adrielcafe Bonsai Tree and am trying to implement a filter for a tree. The filter function works fine however when the filtered list decreases in size (i.e. fewer results) it returns 'old' data - the count of the filtered list is however correct - part of result set is from the previous search (stale).
The first search for XX returns the correct results
composeFilteredTree:SPARK TRIXX
composeFilteredTree:XXXX
composeFilteredTree:XXXX Bitter
composeFilteredTree:XXXX Gold
composeFilteredTree:XXXX Summer
composeFilteredTree:MaxxMove
treeState:SPARK TRIXX, XXXX, XXXX Bitter, XXXX Gold, XXXX Summer, MaxxMove
The second search for XXX returns the correct count of results (4) but still has old data SPARK TRIXX
composeFilteredTree:XXXX
composeFilteredTree:XXXX Bitter
composeFilteredTree:XXXX Gold
composeFilteredTree:XXXX Summer
treeState:SPARK TRIXX, XXXX, XXXX Bitter, XXXX Gold
i.e. should be XXXX, XXXX Bitter, XXXX Gold,XXXX Summer
#Composable
fun CategoryLayout(viewModel: CategoryViewModel) {
val textState = remember { mutableStateOf(TextFieldValue("")) }
val emptyTree: Tree<Category> = Tree {}
var treeState by remember { mutableStateOf(emptyTree)}
val searchedText = textState.value.text
if (searchedText.isNotEmpty() && searchedText.length >= 2) {
//viewModel.onEvent(CategoryViewModel.UIEvent.SearchChanged(searchedText))
viewModel.filter(searchText = searchedText)
treeState = ComposeFilteredTree(viewModel.filteredCategories.value)
Log.d("", "treeState:${treeState.nodes.joinToString { it -> "${it.name}" }}")
}
else {
treeState = ComposeCategoryTree(viewModel.categories.value)
}
Column {
SearchView(textState)
CategoryTree(treeState)
}
}
#Composable
fun ComposeFilteredTree(categories: List<Category>): Tree<Category> { //= Tree {
return Tree {
for (category in categories) {
Leaf(
content = category,
customIcon = { CustomIcon() },
customName = null,
name = category.text
)
Log.d(
"",
"composeFilteredTree:${category.text}"
)
}
}
}
The Leaf() function of the tree library is a Composable function and therefore needs to be 'built' in the CategoryLayout. The ideal approach would be to observe the tree in a ViewModel but the library instantiation of the tree is in a composable format and I can't seem to figure out how to use the Tree in a ViewModel Class
Extract from library
#Stable
public class Tree<T> internal constructor(
public val nodes: List<Node<T>>
) : ExpandableTree<T> by ExpandableTreeHandler(nodes),
SelectableTree<T> by SelectableTreeHandler(nodes)
#Composable
public fun <T> Tree(
content: #Composable TreeScope.() -> Unit
): Tree<T> {
val applier = remember { TreeApplier<T>() }
val compositionContext = rememberCompositionContext()
val composition = remember(applier, compositionContext) { Composition(applier, compositionContext) }
composition.setContent { TreeScope(depth = 0).content() }
return remember(applier) { Tree(applier.children) }
}
I have a device class in my application where one property needs to be computed and final plus some properties that can be set in the constructor or have default values. Here's the code:
class Device {
String idx;
String id;
String name;
String icon;
int order;
// have to create the idx before initializing the object because I can't
// figure out how to do that here.
Device(
{required this.idx,
required this.id,
required this.name,
this.icon = 'none',
this.order = -1});
// Creates a device from a JSON string
factory Device.fromJson(Map<String, dynamic> jsonData) {
return Device(
idx: jsonData['idx'],
id: jsonData['id'],
name: jsonData['name'],
icon: jsonData['icon'],
order: jsonData['order']);
}
// Returns the device as a String
static Map<String, dynamic> toMap(Device device) => {
'idx': device.idx,
'id': device.id,
'name': device.name,
'icon': device.icon,
'order': device.order
};
}
Basically I'm trying to set a unique index for the object so in my object list I can clearly identify a specific device. I'm using the Uuid package to generate a UUID for idx.
The only way I can make this work today is to create the idx in my other code that creates the object and pass it in. I read a lot of articles here that talk about different ways to solve this problem and I know I have to make the idx value a constant but I can't figure out how to do that and call the Uuid library.
I know it would look something like this:
Device(
{this.idx = const <<some calculation/expression>>,
required this.id,
required this.name,
this.icon = 'none',
this.order = -1});
removing the required modifier and putting a const before the value assignment. Nothing I've tried lets me call the Uuid method. Can someone help me understand how to do this?
Updating the code based on the answer from #jamesdlin:
import 'package:uuid/uuid.dart';
const uuid = Uuid();
class Device {
String idx;
String id;
String name;
String icon;
int order;
Device(
{String? idx,
required this.id,
required this.name,
this.icon = 'none',
this.order = -1})
: idx = idx ?? uuid.v1();
// Creates a device object from a JSON string
factory Device.fromJson(Map<String, dynamic> jsonData) {
return Device(
idx: jsonData['idx'],
id: jsonData['id'],
name: jsonData['name'],
icon: jsonData['icon'],
order: jsonData['order']);
}
// Returns the device object as a String
static Map<String, dynamic> toMap(Device device) => {
'idx': device.idx,
'id': device.id,
'name': device.name,
'icon': device.icon,
'order': device.order
};
}
This works, but I don't ever have a use case where I want the idx set manually, so how to I accomplish that? I could leave it like this, but I really want to better understand how to do exactly what I need.
The only way I can make this work today is to create the idx in my other code that creates the object and pass it in.
If you want the object to be able to generate its own UUID, you just can do:
const uuid = Uuid();
class Device {
String idx = uuid.v1(); // Or whatever UUID version you want.
...
or you if you want the caller to have the option to pass in a UUID string, you can use the typical technique of using null to achieve non-const default function arguments:
const uuid = Uuid();
class Device
String idx;
Device({String? idx, ...})
: idx = idx ?? uuid.v1(),
...
Note that attempting to make a const initializer for a UUID makes no sense. const means that the object is a compile-time constant, and furthermore, const objects are canonicalized, so a hypothetical const expression that generated a UUID would end up producing the same String for every Device, which would be the opposite of unique.
Update for you updated question
This works, but I don't ever have a use case where I want the idx set manually, so how to I accomplish that? I could leave it like this, but I really want to better understand how to do exactly what I need.
I don't understand what you mean since you quite obviously do have a use case for setting idx manually (your Device.fromJson factory constructor). If you instead mean that you don't have a use case for code from outside the Device class to manually set idx, then you can add a private constructor with the idx parameter and a public one without:
class Device {
String idx;
String id;
String name;
String icon;
int order;
Device({
required String id,
required String name,
String icon = 'none',
int order = -1,
}) : this._(
idx: uuid.v1(),
id: id,
name: name,
icon: icon,
order: order,
);
Device._({
required this.idx,
required this.id,
required this.name,
required this.icon,
required this.order,
});
// Creates a device object from a JSON string
factory Device.fromJson(Map<String, dynamic> jsonData) {
return Device._(
idx: jsonData['idx'],
id: jsonData['id'],
name: jsonData['name'],
icon: jsonData['icon'],
order: jsonData['order']);
}
}
or, since idx isn't final, .fromJson could assign it a value:
factory Device.fromJson(Map<String, dynamic> jsonData) {
return Device(
id: jsonData['id'],
name: jsonData['name'],
icon: jsonData['icon'],
order: jsonData['order'])
..idx = jsonData['idx'];
}
In Dart, I can dynamically call a function using Function.apply:
Function.apply(foo, [1,2,3], {#f: 4, #g: 5});
gives exactly the same result as
foo(1, 2, 3, f: 4, g: 5).
Question: Does a similar thing exist for instantiating classes?
Expected result would look something like:
class foo {
final String boo;
int? moo;
foo({required this.boo, this.moo})
}
...
var params = {boo: 'A string value', moo: 121};
Class.apply(foo, params);
// Gives the result:
foo(boo: 'A string value', moo: 121);
Function.apply isn't type-safe, so you should avoid using it if you can.
If you really want to use it with a constructor, you can use it with constructor tear-offs (added in Dart 2.15), which are just Functions:
class Foo {
final String boo;
int? moo;
Foo({required this.boo, this.moo});
#override
String toString() => 'Foo(boo: "$boo", moo: $moo)';
}
void main() {
var params = {#boo: 'A string value', #moo: 121};
var result = Function.apply(Foo.new, [], params);
print(result); // Prints: Foo(boo: "A string value", moo: 121)
}
As far as I know, you can make use of static methods if you want to create an instance without using another instance. Here is a sample:
class Foo {
final String boo;
final int moo;
Foo({this.boo, this.moo});
static fromValues({String boo, int moo}) {
return Foo(boo: boo, moo: moo);
}
}
void main() {
var params = {#boo: 'A string value', #moo: 121};
var fooObject = Function.apply(Foo.fromValues, [], params);
print(fooObject);
print(fooObject.boo);
print(fooObject.moo);
}
Another way is to add 'call' function to class to make it's objects callable and use an object of the class to create new objects. Here is a sample:
class Foo {
final String boo;
final int moo;
Foo({this.boo, this.moo});
call({String boo, int moo}) {
return Foo(boo: boo, moo: moo);
}
}
void main() {
Foo aFoo = Foo(boo: 'nothing', moo: 0);
var params = {#boo: 'A string value', #moo: 121};
var fooObject = Function.apply(aFoo, [], params);
print(fooObject);
print(fooObject.boo);
print(fooObject.moo);
}
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)
class Club(val name: String, val stadium: String) {
// Produces a textual description of the club (which consists of just the club's name).
override def toString = this.name
}
class Match(val home: String, val away: String) {
...
}
val match1 = new Match(club2, club1)
Why doesn't val match1 work?