我正在尝试与Free Pascal 3.0.4相比,在FreeBSD 12下测试Rust 1.39中的某些语言功能,以比较简单的通用点集,其中包含由字符串键寻址的2D点。不幸的是,泛型类型声明的代码无法在很早的状态下编译,并以以下代码终止:
error[E0106]: missing lifetime specifier
--> src/main.rs:11:31
|
11 | type TPointMap = BTreeMap<&TString, TPoint>;
|
我必须如何重写Rust代码?
详细信息:
为了测试语言行为,我在Rust和Pascal中编写了两个小程序,“语法上”针对相同的上下文。 Pascal程序是一个简单的声明:
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.
程序编译并给我:
$ ./main
X: 1.00 Y:2.00
这是我不正确的Rust版本代码:
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
}
注:由于借用和所有权概念,我知道某些代码行不能在Rust代码中使用。线
match map.get(&key)...
是其中之一。
与freepascal
版本大致相同,TString
可能应该是String
而不是str
。 freepascal
字符串是(depending on some flags)指针,长度和字符堆分配的数组。 (几乎)就是String
。 str
只是字符数组,没有大小限制,因此它总是必须位于某种(胖)指针之后。
一旦进行了更改,只有几件事可以修复代码。 TPointMap
需要使用期限参数,因为它使用了引用类型。参考的生命周期必须来自某个地方,因此我们使TPointMap
在该生命周期中是通用的。
type TPointMap<'a> = BTreeMap<&'a TString, TPoint>;
如果您的用例允许,您也可以考虑简单地使用BTreeMap<TString, TPoint>
。
我们需要做一些转换才能声明key: TString
。字符串文字的类型为'static str
,但是有一种简单的to_string
方法将其转换为String
。
let key: TString = "123".to_string();
最后,在found.X
中有一个错字。
Some(found) => println!("X: {} Y: {}", found.x, found.y),
总共,我们有
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
}
您看到此失败的原因是因为您需要一生的时间作为参考。 Rust目前无法知道您的参考应持续多长时间。
一旦解决此问题,您将遇到无法创建类型为String
的变量的事实,因为它没有大小,并且编译器无法在编译时告知要分配多少空间。
您可以在此处进行的最简单,最小的更改是更改以下几行:
str
和
str
((由于Rust区分大小写,因此您还需要在type TString = &'static str;
中将type TPointMap = BTreeMap<TString, TPoint>;
小写。)
这将告诉编译器您的字符串类型为x
,这是字符串文字的类型,因为它们的寿命与程序一样长。
您还可以通过执行以下操作,使其与任意寿命的found.X
引用一起使用:
&'static str
和
str
但是,在许多程序中,您可能希望插入不限于字符串文字的字符串,并避免在映射的整个生命周期内借用必要的借项,例如,如果要从函数返回映射。在这种情况下,使用拥有的type TString<'a> = &'a str;
对象可能很有意义,这样该对象才能与地图一样长。在这种情况下,您的代码将如下所示:
type TPointMap<'a> = BTreeMap<TString<'a>, TPoint>;