1
1
+++
2
- title = " rspotify – 我的第一个Rust crate"
3
- date = 2018-02-28T20:44:00+ 08:00
4
- lastmod = 2022-02-24T22:16:22+ 08:00
2
+ title = " RSpotify – 我的第一个Rust crate"
3
+ date = 2018-02-28T20:44:00- 08:00
4
+ lastmod = 2024-12-30T22:28:21- 08:00
5
5
tags = [" rust" , " rspotify" ]
6
6
categories = [" rust" ]
7
7
draft = false
@@ -10,7 +10,9 @@ toc = true
10
10
11
11
开发第一个Rust crate 的感受和踩到的坑
12
12
13
- 最近写了人生第一个 Rust crate-- [ rspotify] ( https://github.com/samrayleung/rspotify ) . 虽说并不是什么惊天地,泣鬼神的大作,但是也是我花费了近两个月实现的。
13
+ 最近写了人生第一个 Rust crate -- [ RSpotify] ( https://github.com/ramsayleung/rspotify ) .
14
+
15
+ 虽说并不是什么惊天地,泣鬼神的大作,但是也是我花费了近两个月实现的。
14
16
15
17
现在就来聊聊这个开发过程的感悟和踩到的坑
16
18
@@ -27,18 +29,19 @@ toc = true
27
29
``` python
28
30
def artist_top_tracks (self , artist_id , country = ' US' ):
29
31
""" Get Spotify catalog information about an artist's top 10 tracks
30
- by country.
32
+ by country.
31
33
32
- Parameters:
33
- - artist_id - the artist ID, URI or URL
34
- - country - limit the response to one particular country.
34
+ Parameters:
35
+ - artist_id - the artist ID, URI or URL
36
+ - country - limit the response to one particular country.
35
37
"""
36
38
37
39
trid = self ._get_id(' artist' , artist_id)
38
40
return self ._get(' artists/' + trid + ' /top-tracks' , country = country)
39
41
```
40
42
41
- 但是用 Rust 来实现的时候,问题就来了,因为Rust 是没有缺省参数的。而Rust 处理缺省参数的策略一般是` Builder Pattern ` :
43
+ 但是用 Rust 来实现的时候,问题就来了,因为Rust 是没有缺省参数的。而Rust 处理缺省
44
+ 参数的策略一般是=Builder Pattern=:
42
45
43
46
``` rust
44
47
struct Part1 {
@@ -49,24 +52,24 @@ struct Part1 {
49
52
50
53
impl Part1 {
51
54
fn new () -> Part1 {
52
- Part1 { points : 30_u32 , tf : 3_f64 , dt : 0.1_f64 }
55
+ Part1 { points : 30_u32 , tf : 3_f64 , dt : 0.1_f64 }
53
56
}
54
57
55
58
fn tf (mut self , tf : f64 ) -> Self {
56
- self . tf = tf ;
57
- self
59
+ self . tf = tf ;
60
+ self
58
61
}
59
62
fn points (mut self , points : u32 ) -> Self {
60
- self . points = points ;
61
- self
63
+ self . points = points ;
64
+ self
62
65
}
63
66
fn dt (mut self , dt : f64 ) -> Self {
64
- self . dt = dt ;
65
- self
67
+ self . dt = dt ;
68
+ self
66
69
}
67
70
fn run (self ) {
68
- // code here
69
- println! (" {:?}" , self );
71
+ // code here
72
+ println! (" {:?}" , self );
70
73
}
71
74
}
72
75
@@ -78,7 +81,7 @@ Part1::new().tf(7_f64).dt(15_f64).run();
78
81
具体情况具体分析,就 rspotify 而言, ` Builder Pattern ` 并不适用,因为 rspotify
79
82
有很多函数都需要缺省参数,而不同函数的缺省值可能又不一样。
80
83
81
- 例如,有些函数的 ` offset ` 参数是 0, 而另外一些函数的 ` offset ` 参数是1. 为此,我还在 [ Reddit] ( https://www.reddit.com/r/rust/comments/7u1zjl/question_about_default_values_for_function/ ) 发贴询问意见,[ PM_ME_WALLPAPER] ( https://www.reddit.com/user/PM_ME_WALLPAPER ) 建议我用` Into< Option<T>> ` :
84
+ 例如,有些函数的 ` offset= 参数是 0, 而另外一些函数的 = offset ` 参数是1. 为此,我还在 [ Reddit] ( https://www.reddit.com/r/rust/comments/7u1zjl/question_about_default_values_for_function/ ) 发贴询问意见,[ PM_ME_WALLPAPER] ( https://www.reddit.com/user/PM_ME_WALLPAPER ) 建议我用= Into& lt ; Option& lt ; T & gt ;& gt ; = :
82
85
83
86
``` rust
84
87
fn foo <T : Into <Option <usize >>>(limit : T ) {
@@ -102,17 +105,17 @@ pub fn artist_top_tracks(
102
105
url . push_str (& trid );
103
106
url . push_str (" /top-tracks" );
104
107
match self . get (& mut url , & mut params ) {
105
- Some (result ) => {
106
- // let mut albums: Albums = ;
107
- match serde_json :: from_str :: <FullTracks >(& result ) {
108
- Ok (_tracks ) => Some (_tracks ),
109
- Err (why ) => {
110
- eprintln! (" convert albums from String to Albums failed {:?}" , why );
111
- None
112
- }
113
- }
114
- }
115
- None => None ,
108
+ Some (result ) => {
109
+ // let mut albums: Albums = ;
110
+ match serde_json :: from_str :: <FullTracks >(& result ) {
111
+ Ok (_tracks ) => Some (_tracks ),
112
+ Err (why ) => {
113
+ eprintln! (" convert albums from String to Albums failed {:?}" , why );
114
+ None
115
+ }
116
+ }
117
+ }
118
+ None => None ,
116
119
}
117
120
}
118
121
```
@@ -124,9 +127,10 @@ pub fn artist_top_tracks(
124
127
125
128
对于一个 library 而言,错误处理是设计的重要一环。
126
129
127
- 因为我之前只有开发应用的经验, 而开发应用的错误处理和开发类库的错误处理显然需要考虑的东西不一样,所以我还谨慎思考过这个问题。后来,我决定不处理调用Spotify API 或者其他操作导致的错误,将错误进行一次包装(wrap), 然后再返回给library 的调用者。
130
+ 因为我之前只有开发应用的经验, 而开发应用的错误处理和开发类库的错误处理显然需要考虑的东西不一样,所以我还谨慎思
131
+ 考过这个问题。后来,我决定不处理调用Spotify API 或者其他操作导致的错误,将错误进行一次包装(wrap), 然后再返回给library 的调用者。
128
132
129
- 最开始的时候,我是自己定义错误类型的,后来觉得过于累赘,就用上` error_chain ` . 用上 error_chain 之后, ` errors.rs ` 这个文件也非常简单:
133
+ 最开始的时候,我是自己定义错误类型的,后来觉得过于累赘,就用上= error_chain= . 用上 error_chain 之后, = errors.rs= 这个文件也非常简单:
130
134
131
135
``` rust
132
136
/// The kind of spotify error.
@@ -136,27 +140,28 @@ error_chain! {
136
140
errors {}
137
141
138
142
foreign_links {
139
- Json (serde_json :: Error ) #[doc = " An error happened while serializing JSON" ];
143
+ Json (serde_json :: Error ) #[doc = " An error happened while serializing JSON" ];
140
144
}
141
145
}
142
146
```
143
147
144
- 而刚刚我提到了只对错误作简单的包装,得益于 ` error_chain ` 的设计,这个特性也很容易实现:
148
+ 而刚刚我提到了只对错误作简单的包装,得益于 = error_chain= 的设计,这个特性也很容易实现:
145
149
146
150
``` rust
147
151
pub fn convert_result <'a , T : Deserialize <'a >>(& self , input : & 'a str ) -> Result <T > {
148
152
let result = serde_json :: from_str :: <T >(input )
149
- . chain_err (|| format! (" convert result failed, content {:?}" ,input ))? ;
153
+ . chain_err (|| format! (" convert result failed, content {:?}" ,input ))? ;
150
154
Ok (result )
151
155
}
152
156
```
153
157
154
- 这个函数是将 Spotify 的响应体映射成对应的 object(例如 playlist, album 等). 如果转换过程出错了,那么就返回` convert result failed, content {:?} ` 错误信息之后,返回 ` serde_json ` 转换时出现的错误信息。
158
+ 这个函数是将 Spotify 的响应体映射成对应的 object(例如 playlist, album 等). 如果转换过程出错了,那么就返回= convert result failed, content {:?}= 错误信息之后,返回 ` serde_json ` 转换时出现的错误信息。
155
159
156
160
157
161
### <span class =" section-num " >1.3</span > Reddit+clippy {#reddit-plus-clippy}
158
162
159
- 剩下的是在纠结定义一个函数传参的时候是传值,参数是 mutable 还是 immutable, 以及其他类似的考虑。
163
+ 剩下的是在纠结定义一个函数传参的时候是传值,参数是 mutable 还是 immutable, 以及
164
+ 其他类似的考虑。
160
165
161
166
或许 Effective Rust 和 More Effective Rust 出现之后,我读完就知道什么样的设计才是 best practice. 因为有诸多设计的不确定,所以在完成rspotify 90% 的代码量之后,我在 [ Reddit] ( https://www.reddit.com/r/rust/comments/7xn9mh/my_first_crate_rspotify_spotify_api_wrapper/ )
162
167
上发贴,邀请社区的同学来 review code 以帮我完善代码。
@@ -175,7 +180,7 @@ pub fn convert_result<'a, T: Deserialize<'a>>(&self, input: &'a str) -> Result<T
175
180
176
181
虽说 Rust 也有Debugger-- gdb-rust. gdb 我以前写c 的时候用过,gdb 熟悉程度虽然谈 不上精通,但是也能熟练使用。但是用gdb-rust 调试并不是非常便利,比如在使用 [ Rocket] ( https://rocket.rs/ ) 这个Web框架的时候,就很难使用gdb来调试Web程序。
177
182
178
- 虽说 [ intellij-rust] ( https://intellij-rust.github.io/ ) 这个 Intellij Idea 的插件也支 持Debugger, 但是只有配合[ Clion] ( https://www.jetbrains.com/clion/ ) 才能使用。因为 只有 Clion 才能调用 gdb, 无奈。所以在开发 rspotify 的时候,我用得都是 ` println!() ` 调试大法。
183
+ 虽说 [ intellij-rust] ( https://intellij-rust.github.io/ ) 这个 Intellij Idea 的插件也支 持Debugger, 但是只有配合[ Clion] ( https://www.jetbrains.com/clion/ ) 才能使用。因为 只有 Clion 才能调用 gdb, 无奈。所以在开发 rspotify 的时候,我用得都是 = println!()= 调试大法。
179
184
180
185
181
186
### <span class =" section-num " >2.2</span > 编译器Bug {#编译器bug}
0 commit comments