I am trying to test some language features in Rust 1.39 under FreeBSD 12 in comparison to Free Pascal 3.0.4 for a simple generic collection of points with 2D Points addressed by string keys. Unfortunately the code for the generic type declaration does not compile in a very early state and stops with:
error[E0106]: missing lifetime specifier
--> src/main.rs:11:31
|
11 | type TPointMap = BTreeMap<&TString, TPoint>;
|
How do I have to rewrite the Rust code?
Details:
To test the language behavior I've written two small programs in Rust and Pascal addressing "syntactically" the same context. The Pascal program is a straightforward declaration of:
STDIO program test;
uses fgl; { Use the free pascal generics library }
type
TDouble = Double; { Define a 64 bit float }
TString = String; { Define a string type }
TPoint = record { Define a point record }
X : TDouble; { Coordinate X }
Y : TDouble; { Coordinate Y }
end;
{ Define a map of points with strings as key }
TPointMap = specialize TFPGMap<TString, TPoint>;
{ Test program }
var
map : TPointMap; { Declare the map variable }
point : TPoint; { Declare a point variable }
found : TPoint; { Varaiable for a found point }
key : TString; { Variable to address the point in the map }
begin
map := TPointMap.create; { Allocate a new ma container }
with point do begin { Set the point variable }
x := 1.0; y := 2.0;
end;
key := '123'; { Set the key address to '123' }
map.add(key,point); { Store the point in the map }
{ Search the point an write the result in the rusty way }
case map.TryGetData(key, found) of
true : writeln('X: ',found.X:2;, ' Y:', found.Y:2:2);
false : writeln('Key ''',key,''' not found');
end;
map.free; { De-allocate the map }
{ Plain types are de-allocated by scope }
end.
The program compiles and gives me:
$ ./main
X: 1.00 Y:2.00
Here is my incorrect Rust version of the code:
use std::collections::BTreeMap; // Use a map from the collection
type TDouble = f64; // Define the 64 bit float type
type TString = str; // Define the string type
struct TPoint { // Define the string type
x: TDouble, // Coordinate X
y: TDouble, // Coordinate Y
}
// Define a map of points with strings as key
type TPointMap = BTreeMap<&TString, TPoint>;
// Test program
fn main() {
let point = TPoint { x: 1.0, y: 2.0 }; // Declare and define the point variable
let mut map = TPointMap::new(); // Declare the map and allocate it
let key: TString = "123"; // Declare and define the address of point
map.insert(&key, point); // Add the point to the map
// search the point and print it
match map.get(&key) {
Some(found) => println!("X: {} Y: {}", found.X, found.y),
None => println!("Key '{}' not found", key),
}
// map is de-allocated by scope
}
Remark: I'm aware due to the borrowing and ownership concept, that some code lines cannot be used in the Rust code. The line
match map.get(&key)...
is one of them.
To be the rough equivalent of the freepascal version, TString should probably be String rather than str. A freepascal String is (depending on some flags) a pointer, a length and a heap-allocated array of characters. That's (pretty much) exactly what String is. str is just the array of characters and is unsized so it always has to be behind some kind of (fat) pointer.
Once that change is made, there are only a few other things to fix the code. TPointMap needs a lifetime parameter since it uses a reference type. The lifetime on the reference has to come from somewhere, so we make TPointMap generic in that lifetime.
type TPointMap<'a> = BTreeMap<&'a TString, TPoint>;
You might also consider simply using BTreeMap<TString, TPoint> if your use-case allows for it.
We need to do a bit of conversion to declare key: TString. String literals have type 'static str, but there's a simple to_string method to convert them to Strings.
let key: TString = "123".to_string();
Finally, there's a typo in found.X.
Some(found) => println!("X: {} Y: {}", found.x, found.y),
Altogether, we have
use std::collections::BTreeMap; // Use a map from the collection
type TDouble = f64; // Define the 64 bit float type
type TString = String; // Define the string type
struct TPoint {
// Define the string type
x: TDouble, // Coordinate X
y: TDouble, // Coordinate Y
}
// Define a map of points with strings as key
type TPointMap<'a> = BTreeMap<&'a TString, TPoint>;
// Test program
fn main() {
let point = TPoint { x: 1.0, y: 2.0 }; // Declare and define the point variable
let mut map = TPointMap::new(); // Declare the map and allocate it
let key: TString = "123".to_string(); // Declare and define the address of point
map.insert(&key, point); // Add the point to the map
// search the point and print it
match map.get(&key) {
Some(found) => println!("X: {} Y: {}", found.x, found.y),
None => println!("Key '{}' not found", key),
}
// map is de-allocated by scope
}
(playground)
See also What are the differences between Rust's String and str?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With