Olá. Não imediatamente, mas adorei Rust. E esse amor me levou aos mares infinitos do código sem lei. Sobre o que consegui encontrar - sob o corte.
Tipo de dado secreto
Se você leu o Rust Book, provavelmente se lembra de um código de snippet semelhante:
fn unwrap<T>(option: Option<T>) -> T{
let unwrapped = match option{
Some(val) => val,
None => panic!("This cannot be None!")
};
return unwrapped;
}
fn main() {
let unwrapped = unwrap(Some(0));
}
Claro, não há nada incomum aqui. Retorne o valor dentro de Option, se houver, ou chame o término do processo usando a macro panic !. Mas você já se perguntou por que esse código compila ? Como o compilador sabe que uma função que retorna T pode retornar ... isso?
"!" . ? :
#![feature(never_type)]
use std::convert::TryInto;
#[derive(Debug)]
enum ConnectionError{
BrokenPipe,
BadId,
Other
}
struct Client;
struct Request;
struct Response;
impl Request{
pub fn build_response(&self) -> Response{
Response
}
}
fn get_request(id: i32) -> Result<(Client, Request), ConnectionError>{
match id % 2 == 0{
true => {
Ok((Client, Request))
},
false => {
Err(ConnectionError::BadId)
}
}
}
fn init_server() -> Result<!, ConnectionError>{
loop {
let (client, request) = get_request(5i32)?;
let resp = request.build_response();
};
}
fn main() {
let x: ! = init_server().unwrap();
}
, , nightly , "!" "()":
fn init_server() -> Result<(), ConnectionError>{
loop {
let (client, request) = get_request(5i32)?;
let resp = request.build_response();
};
}
fn main() {
let x = init_server().unwrap();
}
? , :
fn main() {
match init_server(){
Ok(v) => { println!("unreachable? {:?}", v); },
Err(_) => {}
};
}
, Ok(v) - . , , . , , , .
? , v
"". "!" , break
, continue
std::process::exit
.
, , . #![feature(never_type)]
? , , , . , , . panic, expect, todo unimplemented. "!"?
, . , .
Rust ( - , ) Fn
. - - ("closures" "", ), , . ?
, , , impl Trait. , , ...
use std::any::type_name;
fn type_of<T>(x: T) -> &'static str {
type_name::<T>()
}
fn callback() -> impl Fn(f32) -> f32{
|a| {
a*2.
}
}
fn main() {
let x = callback();
println!("{}", type_of(x));
}
: playground::callback::{{closure}}
. , , impl Fn(f32) -> f32
, , . , trait object, dyn. - , trait object, Box:
fn main() {
let x: Box<dyn Fn(f32) -> f32> = Box::new(callback());
println!("{}", type_of(x));
}
:
alloc::boxed::Box<dyn core::ops::function::Fn<(f32,)>+Output = f32>
: , , .
, :
use tokio; // 1.0.2
use tokio::task::JoinError;
use futures::prelude::*; // 0.3.12
async fn job1(){}
async fn job2(){
for i in 0..5{}
}
async fn job() -> Vec<impl Future<Output = Result<(), JoinError>>>{
vec![
tokio::spawn(async move{
job1().await;
}),
tokio::spawn(async move{
job2().await;
})]
}
#[tokio::main]
async fn main() {
let mut v = job();
}
, - tokio::spawn
tokio::task::JoinHandle
. , JoinHandle - , , async{}
, , async-, ?
let v = vec![
Box::new(async{}),
Box::new(async{
let cb = |x| x*2.;
let val = cb(1f32);
})
];
, , , ? . , . , .
A ferrugem, por melhor que seja, pode às vezes ser instigante. Por que não retém a capacidade de mudança? Por que o cálculo funcional foi preguiçoso? Por que o cargo cria pastas estranhas com hashes para todas as ocasiões, em vez de construir as mesmas bibliotecas uma vez (embora, para ser justo, isso não seja um problema de linguagem em si)? Seja como for, se escrever nas vantagens é dar um tiro no próprio pé, então escrever rápido é tentar dar um tiro no próprio pé (e Deus me livre de usar ffi no projeto, então as tentativas podem ser bem sucedidas) .
O objetivo deste artigo é tentar se aprofundar na linguagem, entender como ela funciona por dentro, porque, como você sabe, só se pode amar quem se entende.