Những điều tôi đã học được khi viết lib với Rust
Rust
27
facebook
18
accountkit
2
library
2
White

Giang Nguyen viết ngày 25/10/2016

Tuần rồi có ngồi viết một wrapper lại api của Facebook Accountkit bằng Rust, đây là lần đầu viết lib nên mình chọn cái đơn giản này chỉ có 4 endpoint thôi, 3 GET và 1 DELETE thông qua url, không có nhiều require, nên khá đơn giản cho ai tập tành viết lib như mình. Cứ tưởng là đơn giản, nhưng mà khí bắt tay vào làm thì có nhiều vấn đề xảy ra, không đơn giản như những ngôn ngữ khác javascript, php, ruby, ...

Mọi công việc đều đơn giản cho tới khi bạn bắt đầu làm nó. Ngay cả khi viết bài :smile:

Chọn &str hoặc là String

Nếu những ngôn ngữ khác bạn có thể tuỳ ý truyền kiểu String vào, tuy nhiên ở Rust sẽ phân biệt &str là một string slices (fixed len) và String a UTF-8 encoded, growable string, bạn có thể append một str hoặc một char vào một String bất kỳ, còn với str do là fixed len nên bạn không thể append ra được, hiểu rộng ra hơn nếu khai bao kiểu String khi bạn append vào String thì sẽ có quá trình realloccation String đó, nghe có vẻ là không tốt mấy.

String cung cấp cho ta một function là with_capacity

   let will_append = String::with_capacity(20);
   will_append.push_str("hello");

với hàm with_capacity thì sẽ không có quá trìnhreallocation cho đến khi capacity vượt qua giá trị chúng ta khai báo ở đây là 20.

Vậy chọn kiểu gì dể dùng bây giờ &str hay là String, điều đó tuỳ thuộc vào mục đích của bạn, nếu bạn không muốn ownership của params truyền vào thì nên dùng kiểu là &str, nếu muốn là owner của params thì nên dùng kiểu String tuy nhiên sẽ tốt hơn nếu chúng ta biết được len của String chúng ta dùng là gì để dùng với with_capacity sẽ tốt hơn rất nhiều.

Cho nên không muốn kiểu gì thì dùng đại kiểu đó nhé, tất cả điều có lý do.

Functions có thể nhận nhiều kiểu dữ liệu

Điều này giúp cho function của chúng ta trở nên generic hơn, cũng một phần làm hạn chế lỗi xảy ra nếu truyền sai type. Nói hơi khó hiểu để lấy ví dụ minh hoạ

pub struct AccountKit<'a> {
    auth: &'a str,
    appsecret_proof: Option<&'a str>,
}

impl<'a> AccountKit<'a> {
    pub fn with_token<S: Into<Option<&'a str>>>(access_token: &'a str,
                                                appsecret_proof: S)
                                                -> AccountKit<'a> {
        AccountKit {
            auth: access_token,
            appsecret_proof: appsecret_proof.into(),
        }
    }
 }

mình khai báo property appsecret_proof là kiểu Option<str>, xét trường hợp impl của AccountKit thay vì chọn kiểu generic S, ở đây mình sửa lại chút

impl<'a> AccountKit<'a> {
    pub fn with_token(access_token: &'a str,
                                                appsecret_proof: Option<&'a str>)
                                                -> AccountKit<'a> {
        AccountKit {
            auth: access_token,
            appsecret_proof: appsecret_proof,
        }
    }
 }

Nếu như trường hợp ở trên chúng ta chỉ có thể truyền vào kiểu Option, nếu truyền vào kiểu &str thì fail ngay, function tới đây thì có thể dùng ổn được rồi. Tuy nhiên mở rộng ra chúng ta muốn truyền kiểu &str hoặc kiểu Option thì hàm không dùng generic không đáp ứng yêu cầu của chúng tay.

Quay trở lại với hàm generic ở trên, tại sao lại làm được như vậy thì đơn giản là từ bản 1.12 rust có một feature nhỏ là

Option implements From for its contained type

Chúng ta có thể convert từ kiểu &str sang kiểu Option<&str> nếu có một
implement từ &str cho Option<&str>, thế thì tra ở trong trait From xem có không? Rất may mắn có implement đó

impl<'a> From<&'a str> for Cow<'a, str>

Dó đó từ bản 1.12 thì khi function của hcungs ta flexible hơn kiểu nhận vào. Ví dụ

Xử lý lỗi

Thông thường chúng ta có thể handling error với ifelse, hoặc ngay cả dùng unwrap. Nếu dùng unwrap thì chỉ phù hợp với các code muốn quick test, còn production không nên dùng vì nó có panic làm crash luôn chương trình đang chạy, còn dùng if, else nếu với đoạn code dài thì làm cho chúng ta không kiểm soát được đủ hết.

Còn một cách khác là dùng macro try!, khi dung try! thì bắt buộc chúng ta phai khai báo kiểu Error trả về, vì bản thân try! sẽ return về lỗi và được catch ở phần khai bao lỗi của ta trước đó. Và try! được dùng với những function có kiểu trả về là Result.

Như implement dưới đây

pub trait DoRequest: BaseRequest {
    fn retriev_json(&self) -> Result<AccountKitResponse, AccountKitError> {
        let url = try!(Url::parse(self.url()));
        let fresh_net = try!(request::Request::new(self.method(), url));
        let streaming_req = try!(fresh_net.start());
        let mut response = try!(streaming_req.send());
        let mut s = String::new();
        let status = response.status;
        let status_code = status.to_u16();
        let reason = status.canonical_reason();
        try!(response.read_to_string(&mut s));
        Ok(AccountKitResponse {
            body: s,
            status_code: status_code,
            canonical_reason: reason,
        })
    }
 }

Bỏ qua những thứ khác chú ý tới phần AccountKitError, đây là enum tập hợp tất cả error chúng ta tự định nghĩa.

 pub enum AccountKitError {
    HttpError(error::Error),
    UrlError(ParseError),
    IoError(io::Error),
}

Thích khai báo gì thì khai báo ở đây, trường hợp này chúng tự định nghĩa lại error ở lib chúng ta nhưng dựa trên error đã được xử lý ở third party.

Khi dùng try! thì nó sẽ tự động return về đúng case lỗi của chúng ta như ví dụ try!(Url::parse(self.url())) nó sẽ trả về enum AccountKitError::UrlError(ParseError) để thấy được chi tiết lỗi là gì chúng ta cần implements 2 trait fmt::Displayconvert::From

impl From<error::Error> for AccountKitError {
    fn from(err: error::Error) -> AccountKitError {
        AccountKitError::HttpError(err)
    }
}

impl From<ParseError> for AccountKitError {
    fn from(err: ParseError) -> AccountKitError {
        AccountKitError::UrlError(err)
    }
}

impl From<io::Error> for AccountKitError {
    fn from(err: io::Error) -> AccountKitError {
        AccountKitError::IoError(err)
    }
}

impl fmt::Display for AccountKitError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            AccountKitError::HttpError(ref err) => write!(f, "{}", err),
            AccountKitError::UrlError(ref err) => write!(f, "{}", err),
            AccountKitError::IoError(ref err) => write!(f, "{}", err),
        }
    }
}

Nếu có một trường hợp lỗi khác thì chúng ta chỉ cần khai báo kiểu lỗi của chúng ta là gì.

Ngoài ra với trường hợp trên chúng ta có thể code theo kiểu

let url = match Url::parse(self.url())) {
    Ok(url) => url,
    Err(err) => return Err(AccountKit::UrlError(e))
};

Tuy nhiên khi so sánh với đoạn code này với việc dùng try! thì ai cũng chọn try! vì nó ngắn gọn và flexible trong việc return Error

Tóm lại

Code Rust khá là thú vị, phải suy nghĩ nhiều và lựa chọn nhiều, nhưng khi đã code được bạn sẽ rất thích nó. Ngoài ra bạn có thể dùng thử lib này rust-accountkit và nếu bạn contribute nó thì thật là tuyệt vời.

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Giang Nguyen

20 bài viết.
34 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
30 5
(Ảnh) Hai ngày nay mình đã tìm hiểu về Amazon S3, Cloudfront và Letsencrypt để xây dựng 2 trang web static, thứ nhất là trang chủ của (Link) và t...
Giang Nguyen viết gần 2 năm trước
30 5
White
9 0
Đôi dòng về ID3 (Ảnh) Nếu bạn nào chưa biết thì có thể đọc phần này, hoặc biết rồi thì có thể next tới phần kế tiếp nhé. 1. Giới thiệu Như hì...
Giang Nguyen viết hơn 1 năm trước
9 0
White
8 0
Type Result Type trên dùng để làm gì? Result được dùng cho những trường hợp chúng ta muốn return lại một giá trị nào đó (Ok) hoặc propagating erro...
Giang Nguyen viết hơn 1 năm trước
8 0
Bài viết liên quan
White
3 3
Hôm nay tự viết lại cái note này về quá trình học Rust, xem như là tự giúp mình nhớ sâu và rõ hơn về Rust. Note này sẽ ngắn gọn hơn bản (Link). Sơ...
Giang Nguyen viết gần 2 năm trước
3 3
White
18 3
Một lỗ hổng bảo mật cực kỳ nghiêm trọng, có ảnh hưởng trực tiếp đến quyền riêng tư của khoảng 1 tỷ tài khoản Facebook và có khả năng ảnh hưởng tới ...
Hùng PV viết gần 2 năm trước
18 3
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
20 bài viết.
34 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!