Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
foo(x);
bar(y);
fn write_info(info: &Info) -> io::Result<()> {
let mut file = try!(File::create("my_best_friends.txt"));
// Early return on error
try!(file.write_all(format!("name: {}\n", info.name).as_bytes()));
try!(file.write_all(format!("age: {}\n", info.age).as_bytes()));
try!(file.write_all(format!("rating: {}\n", info.rating).as_bytes()));
Ok(())
}
try!!, который анализирует вложенные в него выражения и оборачивает все вызовы, возвращающие Result, в match + return.try
{
...
}
catch (Exception e)
{
Logger.Log(e.GetType().Name, e.Message, e.StackTrace);
}
try {
innocent_looking_function();
}
catch ( const boost::exception& e ) {
// Everyone uses boost
handle_error( boost::diagnostic_information( e ) );
}
catch ( const std::exception& e ) {
// Everyone uses std
handle_error( e.what() );
}
catch ( const CException& e ) {
// The library provider has defined his own CException thrown by reference
handle_error( e.what() );
}
catch ( CException* e ) {
// But there is a 20 year old MFC stuff as well; do include magic in order to compile
handle_error( e->Text() );
e->Delete();
}
catch( ... ) {
// I have no idea what else can be thrown
handle_error(_T("No idea what was thrown"));
}
fn do_work(connection: &mut Connection) -> Result<(), SqlError> {
let mut txn = connection.begin();
try!(txn.insert(...));
try!(txn.update(...));
...
txn.commit()
}
connection.begin() возвращает объект, у которого в деструкторе транзакция отменяется. Если этот объект выходит из области видимости, то транзакция отменяется. Макрос try!() как раз обеспечит ранний выход из области видимости. Метод commit() «поглощает» объект (принимает по значению и перемещает в себя), выполняя коммит транзакции. Поскольку txn уходит «во внутрь» метода, он не будет уничтожен прямо здесь, и роллбэка не произойдёт.А как в Rust с управлением памятью? Есть GC?
assert_eq!(Ok(2).and_then(sq).and_then(sq), Ok(16));
assert_eq!(Ok(2).and_then(sq).and_then(err), Err(4));
assert_eq!(Ok(2).and_then(err).and_then(sq), Err(2));
assert_eq!(Err(3).and_then(sq).and_then(sq), Err(3));

Программа либо устанавливала флаг ошибки, либо возвращала код, который проверялся вызывающей стороной.
…
У обоих решений имеется общий недостаток: они загромождают код на стороне вызова. Вызывающая сторона должна проверять ошибки немедленно после вызова. К сожалению об этом легко забыть.
let x = try!(foo());
bar(x);
Качество кода возросло, потому что два аспекта, которые прежде были тесно переплетены — алгоритм отключения устройства и обработка ошибок, — теперь изолированы друг от друга.
Кажется, автор не принимал во внимание алгебраические типы. С ними невозможно «забыть» проверить ошибки, а добавление try! (в простом случае) не особо нагромождает код:
void Run()
{
try
{
var a = A();
Console.WriteLine(a);
}
catch (ArgumentException ex)
{
Console.WriteLine("Something is wrong, message = {0}\tStacktrace={1}", ex.Message, ex.StackTrace);
}
}
int A()
{
return B();
}
int B()
{
return C();
}
int C()
{
return D();
}
int D()
{
return E();
}
int E()
{
return F();
}
int F()
{
throw new ArgumentException();
}
То есть я понимаю, что в софте для каких-нибудь самолетов или АЭС это выполнено, но для какого-нибудь более приземленного программирования вроде какой-нибудь змейки или тетриса обычно этого не делают.
enum Error { ArgumentError, AnotherError, OneMoreError }
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
&Error::ArgumentError => write!(f, "ArgumentError"),
&Error::AnotherError => write!(f, "AnotherError"),
&Error::OneMoreError => write!(f, "OneMoreError"),
}
}
}
fn f() -> Result<i32, Error> {
Err(Error::ArgumentError)
}
fn e() -> Result<i32, Error> {
f()
}
fn d() -> Result<i32, Error> {
e()
}
fn c() -> Result<i32, Error> {
d()
}
fn b() -> Result<i32, Error> {
c()
}
fn a() -> Result<i32, Error> {
b()
}
fn main() {
match a() {
Ok(x) => println!("Result:{}",x),
Err(Error::ArgumentError) => println!("Error caught"),
Err(e) => println!("Error uncaught {}", e),
}
}
На эту тему немало копий сломано, но если от кодов ошибок ушли к исключениям, то наверное это нужно?
На эту тему немало копий сломано, но если от Python ушли к Node.js, то наверное это нужно?
Интересно, а можно ли ввести исключения опционально?И рисковать в будущем поддерживать два варианта интерфейсов: с ADT и с исключениями. В язык легко что-то добавить в «хоть каком-то», в «отключаемом» виде, но потом придётся тащить бремя совместимости.
int method(int x) throws SomeException {
}
fn method(x: i32) -> Result<i32, SomeException> {
}
Вы видите, как и что возвращают функции, и вы не можете взять результат, не проверив на потенциальные ошибки (привет, Go!).В Гоу «panic» есть, который является эквивалентом исключений.
func Open(name string) (file *File, err error)
...
f, err := os.Open("filename.ext")
if err != nil {
log.Fatal(err)
}
Дефолтный аллокатор (тот, что в libstd) сейчас паникует (или завершает программу, не помню точно — в любом случае, обработать его нельзя).
Если вы пишите что-то близкое к железу, то там вы можете реализовать свои способы аллокации памяти (отключив libstd) и самостоятельно обрабатывать такие ошибки.
Iron averages 84,000+ requests per second for hello world and is mostly IO-bound, spending over 70% of its time in the kernel send-ing or recv-ing data.*
* Numbers from profiling on my OS X machine, your milage may vary.
while let Some(val) = iter.next() {
println!("{}", val);
}
fn func<I: Iterator>(iter: &mut Peekable<I>) -> String
Стандартный способ — peek/match/next.
while let Ok(msg) = receiver.try_recv() {
println!("{}", msg);
}
Есть ошибки в borrowing механизме
fn main() {
let v = vec![1, 2, 3, 4];
let mut it = v.iter().peekable();
while let Some(_) = it.peek() {
println!("{:?}", it.next());
}
}
Ржавая очевидность